Пользователь
0,0
рейтинг
20 февраля 2012 в 12:55

Разработка → Как обезопасить исходники своего 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 сэкономит столько же времени, сколько могло бы сэкономить мне.
Roman Sichny @warsoul
карма
13,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (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
              Знакомьтесь, это лиш:
          • 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
          версия cython?
  • 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.

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