как зашифровать скрипт python
Шифрование и криптография в Python
В Python не так уж много инструментов стандартной библиотеки, которые работают с шифрованием. Однако, в нашем распоряжении есть библиотеки хешинга. Давайте рассмотрим этот вопрос в данной статье, но более детально сфокусируемся на двух сторонних пакетах: PyCrypto и cryptography. Мы научимся шифровать и расшифровывать строки при помощи двух этих библиотек.
Хеширование
Если вам нужно защитить хэши или алгоритм дайджеста сообщений, то для этого прекрасно подойдет модуль стандартной библиотеки Python hashlib. Он включает в себя безопасные алгоритмы хеширования FIPS, такие как SHA1, SHA224, SHA256, SHA384, а также SHA512 и MD5. Python также поддерживает функции хеширования adler32 и crc32, но они содержатся в модуле zlib. Одно из самых популярны применений хеширования это хранение хеша пароля, вместо самого пароля. Конечно, хеш должен быть хорошим, в противном случае он может быть расшифрован.
Другой популярный случай, в котором применяется хеширование – это хеширование файла, с последующей отправкой файла и его хеша по отдельности. Получатель файла может запустить хеш в файле, чтобы убедиться в том, что файл соответствует отправленному хешу. Если это так, значит никто не менял файл, когда он был отправлен. Давайте попробуем создать хеш md5. Но оказывается, чтобы использовать хеш md5, нужно передать его строке байта, вместо обычной. Так что мы попробовали сделать это, после чего вызвали метод дайджеста, чтобы получить наш хеш. Если вы претпочитаете хешированный дайджест, мы можем сделать и это:
Шифрование сообщений в Python. От простого к сложному. Шифр Цезаря
Немного о проекте
Мне, лично, давно была интересна тема шифрования информации, однако, каждый раз погрузившись в эту тему, я осознавал насколько это сложно и понял, что лучше начать с чего-то более простого. Я, лично, планирую написать некоторое количество статей на эту тему, в которых я покажу вам различные алгоритмы шифрования и их реализацию в Python, продемонстрирую и разберу свой проект, созданный в этом направлении. Итак, начнем.
Для начала, я бы хотел рассказать вам какие уже известные алгоритмы мы рассмотрим, в моих статьях. Список вам представлен ниже:
Шифр Цезаря
Итак, после небольшого введения в цикл, я предлагаю все-таки перейти к основной теме сегодняшней статьи, а именно к Шифру Цезаря.
Что это такое?
Какими особенностями он обладает?
Программная реализация
В интернете существует огромное множество уроков, связанных с криптографией в питоне, однако, я написал максимально простой и интуитивно понятный код, структуру которого я вам продемонстрирую.
Начнем, пожалуй, с создания алфавита. Для этого вы можете скопировать приведенную ниже строку или написать все руками.
Далее, нам нужно обозначить программе шаг, то есть смещение при шифровании. Так, например, если мы напишем букву «а» в сообщении, тот при шаге «2», программа выведет нам букву «в».
Итак, создаем переменную smeshenie, которая будет вручную задаваться пользователем, и message, куда будет помещаться наше сообщение, и, с помощью метода upper(), возводим все символы в нашем сообщении в верхний регистр, чтобы у нас не было ошибок. Потом создаем просто пустую переменную itog, куда мы буем выводить зашифрованное сообщение. Для этого пишем следующее:
Модернизация
Вот мы и написали программу, однако она имеет очень большой недостаток: «При использовании последних букв(русских), программа выведет вам английские буквы. Давайте это исправим.
Для начала создадим переменную lang, в которой будем задавать язык нашего шифра, а так же разделим английский и русский алфавиты.
Теперь нам надо создать условие, которое проверит выбранный язык и применит его, то есть обратится к нужному нам алфавиту. Для этого пишем само условие и добавляем алгоритм шифрования, с помощью которого будет выполнено шифрование:
Дешифровка сообщения
Возможно это прозвучит несколько смешно, но мы смогли только зашифровать сообщение, а насчет его дешифровки мы особо не задумывались, но теперь дело дошло и до неё.
Для начала, я предлагаю сделать «косметическую» часть нашей переделки. Для этого перемещаемся в самое начало кода:
Остальное можно оставить так же, но если у вас есть желание, то можете поменять названия переменных.
По большому счету, самые ‘большие’ изменения у нас произойдут в той части кода, где у нас находится алгоритм, где нам нужно просто поменять знак «+» на знак «-«. Итак, переходим к самому циклу:
Итоговый вид программы
Итак, вот мы и написали простейшую программу для шифрования методом Цезаря. Ниже я размещу общий вид программы без моих комментариев, чтобы вы еще раз смогли сравнить свою программу с моей:
Вы успешно написали алгоритм шифровки и дешифровки сообщения на Python с помощью метода Цезаря. В следующей статье мы с вами рассмотрим Шифр Виженера, а также разберем его реализацию на Python, а пока я предлагаю вам написать в комментариях варианты модернизации программы(код или просо предложения и пожелания). Я обязательно учту ваше мнение.
Как не продолбать пароли в Python скриптах
Все меняется, когда мы убираем из схемы человека, который старательно вводит ключ с бумажки. При взаимодействии двух информационных систем, на клиентской стороне в любом случае должен храниться пароль в открытом для системы виде, чтобы его можно было передать и сравнить с эталонным хешем. И вот на этом этапе админы обычно открывают местный филиал велосипедостроительного завода и начинают старательно прятать, обфусцировать и закапывать секретный ключ в коде скриптов. Многие из этих вариантов не просто бесполезны, но и опасны. Я попробую предложить удобное и безопасное решение этой проблемы для python. И чуть затронем powershell.
Как делать не надо
Всем знакома концепция «временного скриптика». Вот буквально только данные по-быстрому распарсить из базы и удалить. А потом внезапно выясняется, что скрипт уже из dev-зоны мигрировал куда-то в продакшен. И тут начинают всплывать неприятные сюрпризы от изначальной «одноразовости».
Чаще всего встречается вариант в стиле:
Проблема в том, что здесь пароль светится в открытом виде и достаточно просто обнаруживается среди залежей старых скриптов автоматическим поиском. Чуть более сложный вариант идет по пути security through obscurity, с хранением пароля в зашифрованном виде прямо в коде. При этом расшифровка обратно должна выполняться тут же, иначе клиент не сможет предъявить этот пароль серверной стороне. Такой способ спасет максимум от случайного взгляда, но любой серьезный разбор кода вручную позволит без проблем вытащить секретный ключ. Код ниже спасет только от таких «shoulder surfers»:
Самый неприятный сценарий — использования систем контроля версии, например git, для таких файлов с чувствительной информацией. Даже, если автор решит вычистить все пароли — они останутся в истории репозитория. Фактически, если вы запушили в git файл с секретными данными — можете автоматически считать их скомпрометированными и немедленно начинать процедуру замены всех затронутых credentials.
Использование системного хранилища
Есть крутейшая библиотека keyring. Основной принцип работы строится на том, что у каждого пользователя ОС есть свое зашифрованное хранилище, доступ к которому возможен только после входа пользователя в систему. Она кроссплатформенная и будет использовать тот бэкенд для хранения паролей, который предоставлен операционной системой:
Сравним сложность атаки
При хранении пароля непосредственно в скрипте нужно:
Пример использования
Безопасный ввод пароля
Еще один частый вариант утечки секретных паролей — история командной строки. Использование стандартного input здесь недопустимо:
В примере выше я уже упоминал библиотеку getpass:
Ввод данных при ее использовании аналогичен классическому *nix подходу при входе в систему. Ни в какие системные логи данные не пишутся и не отображаются на экране.
Немного о Powershell
Для Powershell правильным вариантом является использование штатного Windows Credential Locker.
Реализуется это модулем CredentialManager.
Как защитить Python-приложения от внедрения вредоносных скриптов
Python-приложения используют множество скриптов. Этим и пользуются злоумышленники, чтобы подложить нам «свинью» — туда, где мы меньше всего ожидаем её увидеть.
Однако у этой простоты и лёгкости есть и обратная сторона: чем проще вам выполнять код из разных локаций, тем больше у злоумышленника возможностей для вмешательства.
Python-код нужно размещать в безопасных местах
Модель безопасности Python основана на трёх принципах:
В общем, всё и так будет хорошо, если только у вас одного есть полномочия добавлять и редактировать файлы в директориях, из которых Python берёт модули для импорта.
Папка Downloads — уязвимая локация
Существует множество способов заставить браузеры (а иногда и другое программное обеспечение) поместить файлы с произвольными именами в папку Downloads («Загрузки») без ведома пользователя. Эту уязвимость использует атака под названием «Подмена DLL». В нашем случае речь пойдёт о скриптах Python, но идея та же самая.
Браузеры стали более серьёзно относиться к этому и постепенно пытаются сделать так, чтобы посещённые пользователем сайты не могли тайно сбрасывать файлы в его папку Downloads.
Как работает атака
Вроде бы ничего страшного, но, оказывается, две недели назад на совершенно другом сайте, который вы посетили, какой-то XSS JavaScript без вашего ведома загрузил pip.py с вредоносными программами в вашу папку Downloads.
Авария!
Странное поведение PYTHONPATH
Несколькими абзацами выше я писал:
единственная локация (кроме папки с установленным Python), которая будет автоматически добавлена в ваш sys.path по умолчанию, — это папка с главным скриптом или исполняемым файлом.
Так, а что здесь делает словосочетание «по умолчанию»? Какие ещё локации можно добавить?
К сожалению, существует ситуация, при которой вы могли бы сделать это случайно.
Давайте для иллюстрации набросаем «уязвимое» Python-приложение:
Останется только запустить его:
Пока всё идёт неплохо.
Точно, это совсем не безопасно!
Если по каким-то причинам вы этого сделать не можете, то используйте лайфхак:
В bash и zsh это даст вот такой результат:
Ну вот, теперь нет никаких лишних двоеточий и пустых записей.
Проблема гораздо серьёзнее
Существует несколько вариантов опасного развития событий, связанных с запуском Python-файлов из папки Downloads:
Предостережения
В общем, просто избегайте использования Downloads в качестве текущего рабочего каталога и любые скрипты, которые вы хотите использовать, перед запуском переместите в более подходящее место.
Важно понимать, откуда Python берёт код, который будет выполнять. Позволить кому-либо выполнить хотя бы одну строку левого Python-скрипта равносильно предоставлению полного контроля над вашим компьютером!
Производственная необходимость
Читая такую статью с «советами и лайфхаками» по безопасности очень легко предположить, что автор очень умён, знает кучу мелочей, которую и другие должны знать и постоянно думать об этом. Но на самом деле, это не совсем так. Я объясню.
За многие годы работы я с завидной периодичностью наблюдал, как пользователи не понимали, откуда Python загружает код. Например, люди помещают свою первую программу, использующую Twisted, в файл с именем twisted.py. Но ведь в этом случае импорт библиотеки просто невозможен!
Киберпреступники радуются намного больше, если они смогли успешно атаковать системных администраторов или разработчиков. Если вы взломаете пользователя, то получите данные этого пользователя. Но если вы взломаете администратора или разработчика и сделаете это правильно, то вы можете получить доступ к тысячам пользователей, чьи системы находятся под контролем администратора, или даже миллионам пользователей, которые используют программное обеспечение разработчиков.
Поэтому «просто всё время будь осторожнее» не является надёжным рецептом. Те ИТ-специалисты, которые несут ответственность за продукты с большим количеством пользователей, действительно обязаны быть более осторожными. По крайней мере, должны быть проинформированы об особенностях работы тех или иных инструментов разработки.
Баг или фича.
Ничего из того, что я описал выше, на самом деле не является настоящей «ошибкой» или «уязвимостью». Я не думаю, что разработчики Python или Jupyter сделали что-то по ошибке. Система работает так, как она спроектирована, и это, наверное, можно объяснить. Лично у меня нет идей, как можно что-то в ней изменить, не урезая ту мощь Python, за которую мы так его ценим.
Одно из моих любимых изобретений — это SawStop, уникальная система безопасности для настольных пил. Она позволяет останавливать вращение пильного диска в течении 5 миллисекунд — при контакте с человеческим телом. До изобретения этой системы настольные пилы были инструментами чрезвычайно опасными, но они выполняли важную производственную функцию. На настольных пилах было сделано много очень полезных и важных вещей. Тем не менее, верно также и то, что на настольные пилы приходилась непропорционально большая доля несчастных случаев в деревообрабатывающих цехах. Теперь SawStop экономит много пальцев каждый год.
Таким образом, подробно описав сложности при работе со скриптами Python, я также надеюсь натолкнуть на размышления некоторых инженеров по безопасности. Можно ли сделать SawStop для выполнения произвольного кода для интерактивных интерпретаторов? Какое решение могло бы предотвратить некоторые из описанных выше проблем?
Оставайтесь в безопасности, друзья.
На правах рекламы
VDSina предлагает безопасные серверы на Linux или Windows — выбирайте одну из предустановленных ОС, либо устанавливайте из своего образа.
Об одном способе защиты исходников Python-программы
Как всё начиналось
Однажды мне пришлось участвовать в разработке одного небольшого проекта для научных расчётов, который разрабатывался на языке программирования Python. Изначально Python был выбран как удобный и гибкий язык для экспериментов, визуализации, быстрого прототипирования и разработки алгоритмов, но в дальнейшем стал основным языком разработки проекта. Надо заметить, что проект был хоть и не большим, но довольно насыщенным технически. Для обеспечения требуемой функциональности, в проекте широко применялись алгоритмы теории графов, математическая оптимизация, линейная алгебра и статистика. Также использовались декораторы, метаклассы и инструменты интроспекции. В процессе разработки пришлось использовать сторонние математические пакеты и библиотеки, например, такие как numpy и scipy, а также многие другие.
Со временем стало ясно, что переписывать проект на компилируемом языке слишком затратно по времени и ресурсам. Скорость работы и потребление памяти не являлись критичными показателями в данном случае и были вполне приемлемыми и достаточными. Поэтому было принято решение оставить всё как есть, и продолжить разработку и поддержку проекта на языке Python. К тому же, документация по большей части уже была написана с использованием Sphinx.
Проект являлся библиотекой, функции которой использовались в одном из модулей расширения в крупном программном комплексе. Программный комплекс был написан на C++, являлся коммерческим продуктом, имел защиту с аппаратным ключом и поставлялся клиентам без предоставления исходных кодов.
Здесь сразу обозначилась новая проблема: как защитить исходные коды нашей Python-библиотеки? Может быть, в ином случае никто бы не стал этим заниматься, я бы уж точно, но в библиотеке были реализованы некоторые ноу-хау, и руководители проекта не хотели, чтобы данные наработки попали к конкурентам. Так как я был одним из исполнителей, мне пришлось озаботиться данной проблемой. Далее я постараюсь рассказать об основной идее, что из этого вышло, и как нам удалось скрыть Python-исходники от лишних глаз.
Что предлагают люди
Как известно, наверное, большинству разработчиков, Python — язык интерпретируемый, динамический с богатыми возможностями интроспекции. Бинарные файлы модулей *.pyc и *.pyo (байт-код) легко декомпилируются, поэтому распространять их в чистом виде нельзя (если уж мы решили не показывать исходники по-настоящему).
Как, я думаю, любой на моём месте, сначала я решил поискать, а что вообще делают люди в таких случаях? Первые же поисковые запросы показали, что люди не знают, что делать и спрашивают об этом на stackoverflow и в других местах, например, вот вопрос на stackoverflow. Поискав, я пришёл к выводу, что везде предлагают несколько спорных способов:
По многим причинам я отбросил все эти способы как неподходящие. Например, обфускация Python-кода. Ну какая может быть обфускация, когда синтаксис языка построен на отступах, а сам язык пронизан «хитрой интроспекцией»? Транслировать все Python-модули в бинарные модули расширения тоже не представлялось возможным, т. к. проект, напомню, был достаточно сложным технически с использованием множества сторонних пакетов, да и сам состоял из большого числа модулей в многоуровневой иерархии пакетов, которые было утомительно перегонять в *.pyd, а потом ловить баги, вылезающие на ровном месте. Возиться с заменой опкодов не хотелось, так как пришлось бы распространять и поддерживать собственную сборку интерпретатора Python, да ещё и компилировать им Python-модули всех используемых сторонних библиотек.
В какой-то момент мне показалось, что эта идея с защитой Python-исходников бесполезная, надо всё это бросить и убедить руководство, что ничего не выйдет и заняться чем-нибудь полезным. Отдаём *.pyc файлы и ладно, кто там будет разбираться? Убедить не получилось, переписывать библиотеку на C++ никому не хотелось, а проект нужно было сдавать. В итоге всё же кое-что получилось сделать. Об этом, если всё ещё интересно, можно прочитать далее.
Что сделали мы
Что может лучше всего защитить какую-либо информацию на цифровом носителе от посторонних? Я думаю, что это шифрование. Вооружившись этой фундаментальной идеей, я решил, что исходники надо шифровать, а иначе и быть не должно. Для стороннего наблюдателя, который начал проявлять излишний интерес, всё это должно выглядеть как куча непонятных файлов с непонятным содержимым. Вполне себе обфускация, но более продвинутая чем заменять имена переменных и вставлять пустые строчки.
Ход моих мыслей был следующим:
Основная идея, думаю, ясна — это более продвинутая обфускация. Как это сделать? Погуглив, я пришёл к выводу, что сделать это вполне реально и даже достаточно просто. С шифрованием исходников всё понятно, зашифровать и/или обфусцировать файлы можно множеством способов, главное, чтобы там была «каша» и «ничего не понятно», а также всё это должно возвращаться к первоначальному виду неизвестным способом (в случае обфускации). Для приведённого здесь примера я буду использовать Python-модуль base64 для «шифрования». В некритичных случаях можно применять замечательный пакет obfuscate.
Python the Importer Protocol
Итак, вроде бы добрались до сути. Можно привести простой пример. Минимальный пример класса, реализующего Importer Protocol, подходящего для наших целей. Он будет заниматься импортом модулей «зашифрованных» base64 из обычной структуры пакетов (в данном случае для простоты мы просто «зашифровали содержимое» файлов, но никак не меняли их имена и структуру пакетов). Считаем, что расширения файлов для наших модулей будут гордо называться «.b64».
Как это работает? Первым делом при создании экземпляра класса собирается информация о модулях нашей библиотеки, которую мы «шифруем». Затем при загрузке конкретного модуля, читается нужный «зашифрованный» файл, «расшифровывается» и импортируется с помощью средств модуля imp уже из «расшифрованной» текстовой строки. Как использовать данный класс? Очень легко. Буквально, одной строчкой включается возможность импортировать «зашифрованные» исходники нашей библиотеки, а по сути ставится хук на импорт:
где root_pkg_path абсолютный или относительный путь к корневому пакету нашей библиотеки. При этом, убирая данную строчку, мы можем использовать обычные исходники, если они доступны. Всё происходит абсолютно прозрачно, а все изменения происходят в одном месте.
Вот и всё, с этого момента импорт модулей из нашей библиотеки осуществляется с перехватом и «расшифровкой». Наш хук будет дёргаться при любом вызове инструкции import, и если импортируется модули нашей библиотеки, хук будет их обрабатывать и загружать, остальные импорты будут обрабатываться стандартно. Что нам и требовалось для более продвинутой обфускации. Представленный код импортёра и установки хука можно положить уже в *.pyd файл и надеяться на то, что никто не будет его дизассемблировать в надежде понять, что мы тут наворотили. В реальном проекте можно использовать настоящее шифрование, в том числе с использованием аппаратного ключа, что должно повысить надёжность данного метода. Также изменение имён файлов и структуры пакетов может быть полезным для большего запутывания.
Заключение
Возможно, что всё, что тут написано и яйца выеденного не стоит — давно известные истины, либо бред сумасшедшего. Но если вышеописанное кому-то может оказаться полезным, я буду рад. Лично для меня данный опыт был полезен, хотя бы потому, что позволил лучше разобраться в мощной и гибкой системе загрузки и импортирования модулей и Importer Protocol. О тех самых штуках, которые чаще всего не требуются разработчикам для написания программ на Python. Всегда интересно узнать что-то новое.
Спасибо за внимание.
UPD 14.08.2013:
По просьбе tangro сделал минимальный проект, в котором демонстрируется описанный способ, но без настоящего шифрования (применены только некие алгоритмы обратимого преобразования).
Скачать zip-архив можно по ссылке. Нужен Python 2.7 x86 под Windows. Запускать нужно скрипт «test_main.py».
UPD 2:
И более интересный пример, в котором производятся некоторые вычисления. Здесь все импорты и вызовы функций из зашифрованных модулей скрыты в бинарном модуле. Скачать архив можно по ссылке.