Pull to refresh

Python, Django и немного защиты кода

Reading time4 min
Views10K

Пролог…

Привет, Habr! Компания, в которой я работаю, занимается системами умного дома и видеонаблюдения. Услуга работает по форме SaaS. Но недавно было решено продавать и локальное решение. За основу «коробки» было решено взять облако, немного его переделать и вот готов новый продукт…

С переездом с облачных «рельс» возник ряд вопросов. Один из них в том, что весь проект написан на Python‑ язык интерпретируемый на лету и не защищенный от копирования от слова совсем. Второй момент, что в проекте используется Django. Фреймворк со своими зависимостями, которые еще аукнулись нам в процессе поиска решений. То с чем мы столкнулись и к чему пришли далее в статье.

Глава 1. Поиск решения

Как защитить код от изменения и копирования? Как сказал классик: «С этим вопросом я пошел в Интернет». И вот что удалось найти:

Решение 1 - смирись

Многие на форумах на вопрос как защищаетесь отвечают- никак. Аргументируют это тем, что кому надо и так взломают, а у python другая философия. Будь открыт миру и все такое.

Все так, но нашему проекту много лет и не хотелось бы терять наработки. Один нечестный клиент и весь труд насмарку.

Решение 2 - перепиши

Второй по популярности ответ- переписать на компилируемый язык программирования. Кроме Python у нас используется Go. Как вариант рассматривали переписать только важную часть на него, но проект настолько монструозный, что переписать его будет долго и дорого. Бизнес не оценил находку…

Решение 3 - отдавай только .pyc файлы

Как и большинство интерпретируемых языков программирования, Python перед выполнением кода переводит его формат байт-кода(такие файлы имеют формат .pyc и лежат в директории __pycache__). Уже этот байт код исполняется интерпретатором. Идея подхода- вручную “скомпилировать” весь код в байт-код. Удалить исходники и запускать pyc файлы.

Плюсы такого метода:

  • Код становится не читаемым

Минусы:

  • Такой код восстанавливается обратно в идентичном виде. Вплоть до названий переменных.

Пример исполнения.

  1. Дан скрипт 

def foo():
   """Тестовая функция"""
   print("Hello, Habr!")

if __name__ == "__main__":
   foo()
  1. “Компилируем” его

python -m compileall main.py

на выходе получаем

Тут уже подозрительно что мы видим в неизменном виде комментарии и часть функции.

  1. Восстановим код.

Возьмем библиотеку uncompyle6.

uncompyle6 -o . pycache/main.cpython-38.pyc

Получаем все в исходном виде вплоть до комментариев.

Внимание!!! Библиотека работает с кодом максимум 3.8 версии.

Итог данного метода. Если вам нужно быстро и хоть как-то защитить код и при этом рисками можно пренебречь- то этот метод для вас. Но нас этот метод не устроил на тот момент.

Решение 4 - Обфускация кода

Обфуска́ция (от лат. obfuscare — затенять, затемнять; и англ. obfuscate — делать не очевидным, запутанным, сбивать с толку) или запутывание кода — приведение исходного кода или исполняемого кода программы к виду, сохраняющему её функциональность, но затрудняющему анализ, понимание алгоритмов работы и модификацию при декомпиляции. [1]

Суть подхода, что мы делаем код не читаемым, меняя понятные названия переменных на кракозябры. 

Один из самых популярных проектов для обфускации pyarmor

Программа интересная. На простых скриптах все хорошо, но большой проект не удастся обфусцировать обычной версией - стоит ограничение в 32768 байт. Нужна платная. Проект иностранный, а ситуация сейчас… Эх, ну в общем сами понимаете.

Решение 5. Найти готовое решение по защите.

Тут все просто, в свете последних событий все производители таких решений, что мы нашли ушли из России. Если есть отечественные варианты буду признателен в комментариях.

Глава 2. Свой импортер и загрузчик

Перебрав предыдущие варианты мы поняли, что нам нужно:

  • Шифровать код, а не обфусцировать/компилировать и т.д его.

  • Метод должен быть простым и минимально затрагивать старый код

  • Должно быть достаточно сложной чтобы дилетанты вовсе не могли сломать, а опытных если не остановить, то хотя бы затруднить взлом.

  • Должен быть прост в поддержке, ибо нанимать/выделять сеньоров для сопровождения сего никто не даст.

После этого я наткнулся на PEP 302 и статью на хабре(какая ирония). 

Если вкратце, pep 302 добавил новый набор перехватчиков импорта, которые предлагают лучшую настройку механизма импорта Python.  В существующую схему можно внедрить хук нового стиля, что позволяет более точно контролировать, как модули находятся и как они загружаются.

Для его реализации нужно реализовать свой класс-импортер или класс-загрузчик(зависит от задачи), добавить в sys.path_hooks и все. Должно заработать.

Ура! -  воскликнул я. Вот и решение. 

Собрал небольшой пет-проект и вуаля- все работает.

Глава 3. Не все так просто как казалось…

После этого началось внедрение в основной проект. Таким образом нужно было защитить три подпроекта. 

Первый заработал без сучка и без задоринки. Если не считать некоторые проблемы с тестами проект работал.

Настало время второго проекта… 

Во втором проекте в одной из зависимостей использовался модуль inspect.

Hidden text

Модуль inspect предоставляет несколько полезных функций, помогающих получить информацию о живых объектах, таких как модули, классы, методы, функции, трассировки, объекты фреймов и объекты кода.(Дословный перевод:https://docs.python.org/3/library/inspect.html)

Этот модуль к моему удивлению активно был связан с ядром самого языка, написанном на C, при этом не подчинялся стандартному механизму импорта. После недельной битвы с модулем, понял, что придется идти в Си. НО! Вспомнив 4 пункт с ограничениями остановился. Сишников в команде нет, а подкладывать такую свинью коллегам не хочется.

Эпилог

Увы и ах! Но после двух недель изысканий мы остановились на первом варианте, а как альтернативу вариант с “компилированием” в pyc-файлы. Юристы пошли дописывать дополнительные пункты в договор, а мы продумывать что в перспективе переписать на GO.

Какой урок я вынес? Простыми способами Python не защитить, теперь я могу сказать это испытав на своей шкуре. Если у читателя есть навыки и желания, можно окунуться в дебри СИшного кода, но это уже другая история… 

Ссылки:

  1. https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%84%D1%83%D1%81%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B5_%D0%BE%D0%B1%D0%B5%D1%81%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)

Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments16

Articles