Как обезопасить исходники своего python-приложения

Рано или поздно все python-разработчики стают перед выбором: отдать заказчику приложение в исходниках или скрыть их. И вот во втором случае у многих (особенно недавно знакомых с этим прелестным языком) начинаются проблемы: поиск по гуглу, как правило, ничего не дает, идей никаких (или все бредовые).

И что же делать в таком случае?

Первой мыслью было отдавать pyc-файлы. Тогда я ещё не вникал в то, что это такое на самом деле. После нескольких часов проведённых в поисках ответов чем это грозит был сделан единственно возможный вывод: вариант не пройдет. Для python < 2.7 «декомпиляторов» полно бесплатных, а 2.7 и выше за сравнительно небольшие деньги обещают выдать в виде исходных кодов. Да ещё и эта тулза, с которой я за считанные мгновения получил свой код один-в-один.

Вариант сборки в бинарник показался достаточно заманчивым. Вот только, как оказалось, все сборщики (ниже я приведу пример cx_Freeze) фактически только и делают что пакуют .pyc в архив, то есть абсолютно не защищают исходные коды.

А потом меня осенило.

Предложим у нас есть проект проект с такой структурой (это всего лиш пример):

TestModule/__init__.py
TestModule/Config.py
ui/__init__.py
ui/mainwindow.py
ui/loginwindow.py
main.py

Тут сразу хотелось бы отметить два момента:
  1. В файле main.py у нас должен быть фактически только вызов главного модуля, если же там что-то большее — желательно оформить это в отдельный модуль
  2. Файлы __init__.py желательно чтоб были вообще пустые.


Делаем несколько простых манипуляций:
  1. $ sudo apt-get install cython
  2. Создаем в корне проекта файл compile.py:
    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    
    ext_modules = [
        Extension("TestModule.Config", ["TestModule/Config.py"]),
        Extension("ui.mainwindow", ["ui/mainwindow.py"]),
        Extension("ui.loginwindow", ["ui/loginwindow.py"]),
        ]
    
    setup(
        name = 'Test App',
        cmdclass = {'build_ext': build_ext},
        ext_modules = ext_modules
    )
    

  3. Там же (в корне проекта) выполняем
    $ python compile.py build_ext --inplace
  4. теперь можем удалить все файлы в подкаталогах кроме *.so и __init__.py


После проверки на работоспособность все должно работать точно так же как и прежде.
Вот и все, исходники теперь точно никто не прочитает. Правда отдавать приложение пока рано, заказчик ведь не захочет устанавливать и настраивать питон со всеми использованными вами модулями. Потому собираем все в «пакет»:
  1. $ sudo apt-get install cx-freeze
  2. В корне проекта создаем файл pack.py:
    from cx_Freeze import setup, Executable   
    
    setup(   
        name = "Test App",   
        version = "0.1",   
        description = "test",   
        executables = [Executable("main.py")])  
    
  3. $ python pack.py build
  4. Копируем «свои» папки из папки проекта в build/exe.linux-x86_64-2.7
  5. Пробуем запустить полученный бинарник и, если надо, копируем недостающие библиотеки (в моем случае это был PyQt)


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

P.S. Надеюсь кому-то это простое how-to сэкономит столько же времени, сколько могло бы сэкономить мне.
Метки:
Поделиться публикацией
Похожие публикации
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 62
  • 0
    А для пользователей Windows это работает?
    • 0
      По крайней мере должно (cython умеет собирать dll, cx_Freeze без проблемм собирает exe). Сегодня буду эксперементировать — обязательно отпишусь о результатах.
      • 0
        Хмм… А можно как-то собирать виндовую сборку из линукса? А то лениво каждый раз грузить винду в виртуалке для сборки…
        • 0
          Если и да — я не нашел как это сделать. Так что по ходу собирать придеться в 4 разных окружениях.
          • 0
            Значит, нельзя. py2exe не умеет так как пользуется поиском зависимостей по установленной версии — включая поиск по DLLкам. Так что еще важно следить, чтобы ставились системы совместимые с target. Сборка на win7 может не заработать с ходу на winxp — надо вырезать некоторые ошибочно попавшие DLLки. Сборка с P4 может не завестись на P3 — ибо numpy «all-in-one» ставит оптимальную для системы (с SSE3) сборку, а там даже SSE нету. В общем, тот еще бубен.
          • +1
            мы как-то под Wine собираем exe-шки с Nullsoft инсталлятором, но я подробностей не знаю правда
            • 0
              под wine? интересно… надо будет попытаться. что-то как-то про него я не подумать.
        • 0
          py2exe
          pyinstaller
          • 0
            pyinstaller — разве не сжимает pyc, как написал автор выше? Если нет, то это очень хорошо…
            кстати спасибо автору за статью, она очень и очень кстати пришлась.
          • 0
            Получилось собрать на винде небольшое приложение таким образом. Причем numpy cython'ом скомпилировался.
          • +1
            Добавляем еще py2exe, чтобы запускать под виндой если оно кросс.
            Добавляем компиляцию с -O -O0 чтобы еще и удалить докстринги.
            Смотрим на sourceforge.net/projects/decompyle/ вздыхаем, понимаем что все защиты от честных людей.
            • 0
              Decompyle Вам не поможет с модулями собранными как сишные библиотеки ;)
              • 0
                А, действительно, cython же… Жаль, не всё на нём работает. У меня в зависимостях болтается pygtk, matplotlib, numpy и прочие радости — очень хочется ускорить это всё добро, но не работает оно ни на чем кроме ванильки cpython'а.
                • –3
                  А тот же гтк идет не в виде библиотек (.dll, .so)?
                  В случае с PyQt например достаточно всего лиш скопировать библиотеку в папку собранного проекта.
                  • –1
                    Запомните, пожалуйста, в русском языке нет слова «лиш».
                  • 0
                    Кроме .dll и .so есть еще питон-биндинги, которые через всякие cdll цепляются. А еще есть отдельные cpython библиотеки, а не pure-python код. А еще есть pywin32 который доставляет отдельные радости.
            • 0
              Разве все что здесь пишут можно так вот просто декомпилировать — все пробовали?
              • +2
                Да, без проблем оно обратно раскручивается.
                • +2
                  У всего там описанного один и тот же принцип. Оно собирает .pyc и пакует его (как правило в zip). Даже если это будет single-exe — он разбирается.
                • +23
                  Есть у меня подозрение, что что-то не так ©

                  Во-первых, титуальная задача довольно странная. Для динамического языка, которым является питон, одним из основополагающих свойств является полная интроспекция — тоесть возможность для любого фрагмента кода получить и модифицировать по имени класс, метод, аргументы, переменные и так далее. Соответственно, что бы мы не делали с программой, в ней будет сохранена большая часть исходников — чтобы работала интроспекция. Поэтому защитить ничего не получится.

                  Питон — не единственный динамический язык с интроспекцией. Теми же свойствами обладает, среди прочих, JavaScript. И для него задача «защиты» исходного кода изучается уже много лет. Насколько я знаю, на данный момент наиболее популярное решение — обфускация — изменение исходного кода, при котором он сохраняет работоспособность, но имена всех идентфикаторов (классов, методов, переменных и прочего) меняются на «a», «b», «c» и так далее. Такой код можно «декомпилировать» — только человек его все равно не сможет нормально читать.

                  И последнее. Насколько я понял, описанное в статье заклинание использует Cython — некую тулзу, которая позволяет компилировать небольшое подмножество Python в С, а затем получившийся код на C — в исполняемый файл. Насколько я понимаю, поправьте меня если я не прав, у данного подхода два серьезных минуса. Во-первых, компиляции поддаются только простейшие программы на Python — никакая интроспекция в C не переносится. Во-вторых, теряется одно из основных преимуществ питона, кроссплатформенность — скомпилированный код можно будет запустить только на той платформе, под которую он собран.

                  В целом странно все это.
                  • –1
                    Во-первых, компиляции поддаются только простейшие программы на Python

                    C многопоточным gui-приложением (PyQt) проблемм не возикло ;)

                    скомпилированный код можно будет запустить только на той платформе, под которую он собран

                    Иногда лучше собрать под 4 платформы, чем потом найти свой код в приложении конкурента.
                    • +8
                      C многопоточным gui-приложением (PyQt) проблемм не возикло ;)


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

                      Иногда лучше собрать под 4 платформы, чем потом найти свой код в приложении конкурента.


                      Как я уже писал ниже, ИМХО динамический язык с интроспекцией — не лучшее решение для тиражируемого closed-source решения.
                      • 0
                        то ли я чего-то не понимаю, то ли вот они все ограничения.
                        • 0
                          Нет, речь вот об этом: wiki.cython.org/Unsupported
                          • 0
                            Спасибо. Я бы не назвал это жесткими ограничениями.
                            • 0
                              А по ссылке оттуда вот сюда бы еще зайти wiki.cython.org/enhancements
                              я на первое что наткнулся — в неподдержку CEP 307 в 0.14.1, потом попробую 0.15.

                              Еще из «жестких ограничений» имхо 305.
                          • 0
                            К слову, у меня вот есть еще DSLи, которые на ply сделаны. Что-то мне подсказывает, что cython это тоже не прожуёт…
                        • 0
                          Иногда лучше найти свой код в приложении конкурента и подать в суд, чем гадать есть он в приложении конкурента или нет.
                          • –1
                            Вы доверяете судам вашей страны?.. А мы — нет.
                            • +1
                              Да какая разница? Судов других всё равно нет, так что вопрос вообще так не стоит. Что бы вы там не накопмилили, всё равно это можно запихать внутрь другого кода, вопрос только в цене вопроса.
                              • –2
                                Вы правы, любые действия по защите приложения/кода это только увеличение цены взлома/декомпиляции/другого нарушения прав на код. Почему же тогда не подстраховаться лишний раз имея такую возможность?
                            • 0
                              У вас есть положительный опыт в подобного рода судебных разбирательствах в этой стране? Можете привести данные о том, сколько длился процесс, сколько денег было потрачено на разбирательство, и сколько в итоге удалось отсудить?
                        • +11
                          Объясните, в каких случаях требуется не отдавать исходники заказчику?
                          Потому что я на месте заказчика больше никогда бы не стал работать с компанией, которая предоставила продукт, с которым я больше ничего не смогу сделать. И всем знакомым заодно рассказал.

                          Другое дело, когда речь о массовом приложении, но тут уже нет внешнего заказчика, а есть только клиенты.
                          • +1
                            ИМХО, использование динамического языка для тиражируемого closed-source решения — это не лучший выбор. Обычно используют нативное ядро и платформонезависимую логику на динамическом языке. Например, большая часть клиентов Dropbox так сделано. Но при этом если вытащить и декомпилировать из него весь код на питоне, то рабочего приложения мы не получим, потому как ядро — на C/C++/Objective-C/Whatever.
                            • –1
                              Вы прави, имелся в виду именно клиент.
                              • –1
                                правиы конечно же.
                              • 0
                                В промежуточных версиях, как вариант
                              • 0
                                Я отстал от жизни, или cython научился компилить «обычные» python скрипты без какой либо доработки?
                                И еще при таком подходе:
                                Никогда не выносите mainloop (для GUI) в модуль! Я как то три дня бился над тем, почему у меня приложение не работало. Собирал с помощью py2exe, в main.py было лишь import myapp, в котором и был весь код (делал так для того чтобы можно было обновления легко накатывать). Потом прочитал о том что все импорты должны быть завершены.
                                Так что:
                                import myapp
                                myapp.mainloop()
                                • +2
                                  Научился. Только вот беда заключается в том, что тестируется это все на простейших приложениях. В больших проектах, хоть как-то использующих декораторы и интроспекцию, начинают вылезать редкоземельные баги.
                                  • 0
                                    Да, научился. Выше упоминался ряд несерьезных, ИМХО, ограничений.
                                    • +4
                                      Моя задача (кроме получения новой информации для себя лично) — предупредить о потенциальных проблемах этой технологии, исходя из личного опыта. Если бы с компиляцией питона в натив все было хорошо — это бы делали все. Но все это не делают :). Основная задача проекта Cython — облегчение написания нативных расширений для питона. И позиционируется он как вообще другой язык с «близким к пиону синтаксисом». Так что не по назначению инструмент используем, что череповато последствиями.
                                      • +1
                                        Блин, ткнулся для проверки в первый же модуль приложения, и вот те на те ёж в томате:
                                        datacompboy@nuuzerpogodible:~/work/kvarta/himlab$ cython -v src/chrom_analyse.py
                                        Compiling /home/datacompboy/work/kvarta/himlab/src/chrom_analyse.py
                                        chrom_analyse.py:702:26: Generators are not supported
                                        
                                  • 0
                                    Вопрос по билд-системе: cx-freeze сам вызывает cython и gcc для сборки, или нет?
                                    Есть ли готовые авто-системы резолва и сбора зависимостей, чтоб «ext_modules» само собралось?
                                    • 0
                                      Нет, не вызывает.
                                      Насчет авто-резолва не в курсе.
                                      Хотя да, есть мысль что автоматизировать процесс можно при надобности.
                                      • 0
                                        Дописал в стартап-файле
                                        try:
                                            import pyximport
                                            pyximport.install(pyimport = True)
                                        except:
                                            pass
                                        


                                        Получил на выходе пачку варнингов и руганьгов, но оно запустилось! буду копать дальше.
                                    • +1
                                      Есть более и простое решение.

                                      Как Python обрабатывает код? Правильно: исполняет заранее скомпилированный байт-код на виртуальной машине. Поэтому меняем опкоды — и все декомпиляторы ломают зубы в безумном трансе.

                                      1. Качаем исходники с python.org.
                                      2. Ищем файл Includes/opcode.h
                                      3. Меняем значения констант на свои
                                      4. Делаем свою сборку Питона
                                      5. Компилим с помощью нее исходники и распространяем их со своей виртуальной машиной.
                                      6. ???
                                      7. PROFIT!!!
                                      • +1
                                        Во втором пункте: Include/opcode.h
                                        • 0
                                          Статьи с примерами не помешало бы ;)
                                        • 0
                                          ИМХО, это нечестно — закрывать исходный код. Заказчик заплатил Вам деньги (нанял Вас), и код, получается, принадлежит ему, а Вы его не одтаете.
                                          • –1
                                            Да, Вы правы, правильнее наверно было бы написать «клиент». Но статья не о том ;)
                                            • 0
                                              С чего вы взяли что заказчик заплатил за исходный код?
                                              • 0
                                                Заказчик заплатил за разработку продкута. Значит, результат — его собственность. Исходный код, помимо скомпилированных бинарников, и есть результат процесса разработки.
                                                • +1
                                                  Это вы уже додумали. Заказчик мог заплатить за решение задачи. Простой пример, заказали бюст, в процессе изготовление было сделано специальное долото. Для показа бюста долото не нужно. Почему заказчику нужно отдать и инструмент?

                                                  Это может и должно оговариваться отдельно.
                                                  • +1
                                                    На сколько знаю, если с заказчиком, работодателем или еще кем-то заключается договор, в котором явно это указано, то все, что сделал и изобрел работник во время решении поставленной задачи — принадлежит работодателю.
                                                    Но да, вы правы, это должно быть описано в договоре. Плюс такой договор должен быть составлен и подписан.
                                                    • 0
                                                      Все сделанное и изобретенное — касающееся именно самой задачи. Естественно, если во время написания программы программист изобрел новый способ варения пельменей — это левое изобретение.

                                                      Хотя как знать… =)
                                              • 0
                                                От договора зависит.
                                              • 0
                                                Cython, кстати, умеет создавать и исполняемые файлы, а не только библиотеки-модули для импорта. Используя ключ --embed можно обойтись без cx_Freeze.
                                                • 0
                                                  У меня, к сожалению, после запуска бинарника собраного с --embed вываливались segmentation fault.

                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.