За последние 24 часа нас посетили 22400 программистов и 1028 роботов. Сейчас ищут 679 программистов ...

Система лицензирования

Тема в разделе "PHP для профи", создана пользователем Freakmeister, 26 окт 2022.

Метки:
  1. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Я сделал штуку на PHP. Мне надо продавать ключи лицензии, чтобы ключ привязывался к домену и одна штука работала только на одном домене (проверка ключа через API на моём сервере). Как это упаковать от дурака по-простому, чтобы не любой школьник мог это взломать? Задумка такая - у штуки есть фронт-часть и есть админский раздел с настройками. Я сделаю проверку лицензии только в админской части, так что мне не важно на сколько эта "защита от дурака" будет влиять на производительность.
     
    #1 Freakmeister, 26 окт 2022
    Последнее редактирование: 26 окт 2022
  2. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Да кстати, "штука" это плагин для вордпресса. И я тут нашёл какое-то готовое решение: https://freemius.com/
    Но я не хочу его использовать потому что:
    1. Я манал платить деньги за то, что можно сделать самому.
    2. Если залезть в их исходный код, то там файлы по 25 тыщ строк... Они вообще поехавшие? Уверен, что я уложусь в 300 строк.

    upload_2022-10-26_23-4-55.png
     
  3. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Мне хватило 30 строк чтобы проверить лицензию.
    PHP:
    1. <?php
    2. namespace GachiAddons\Admin;
    3.  
    4. defined( 'ABSPATH' ) || exit;
    5.  
    6.  
    7. class ControllerPro {
    8.     // singleton
    9.     private static $instance = null;
    10.     public static function instance() {
    11.         if ( is_null( self::$instance ) ) {
    12.             self::$instance = new self();
    13.         }
    14.         return self::$instance;
    15.     }
    16.  
    17.     private function __construct() {
    18.         if (!GACHI_ADDONS_PRO) {
    19.             return;
    20.         }
    21.    
    22.         $key = 'alsfkl1jrkl1jr2t2jltk2jlkfjlkgt3l4634';
    23.         $licence_check = file_get_contents('https://< ссылка на скрипт проверки ключа>/licence.php?key='.$key.'&referer='.$_SERVER["SERVER_NAME"]);
    24.         $licence_check = json_decode($licence_check);
    25.    
    26.         if (!$licence_check) {
    27.             $licence_check = (object)array('error' => 3);
    28.         }
    29.    
    30.         switch ($licence_check->error) {
    31.             default:
    32.             case 0:
    33.                 $error = '';
    34.                 break;
    35.             case 1:
    36.                 $error = 'Введён неправильный ключ лицензии.';
    37.                 break;
    38.             case 2:
    39.                 $error = 'Планин используется на неправильном домене.';
    40.                 break;
    41.             case 3:
    42.                 $error = 'От сервера проверки лицензий не пришёл ответ.';
    43.                 break;
    44.         }
    45.         define('GACHI_ADDONS_ERROR', $error);
    46.      
    47.         if (GACHI_SETTINGS['gachi_muchi'] ?? false) {
    48.             require_once GACHI_ADDONS_PRO_DIR . '/admin/gachi-muchi.php';
    49.             // здесь дальше ошибка обрабатывается и премиум-функции блокируются
    50.             GachiMuchi::instance();
    51.         }
    52.     }
    53. }

    Проверка на стороне сервера тоже уложилась в 30 строк:
    PHP:
    1. <?php
    2. $key = trim($_GET['key']);
    3. $referer = trim($_GET['referer']);
    4.  
    5. if (!$key) {
    6.     die;
    7. }
    8.  
    9. $data = array(
    10.     'alsfkl1jrkl1jr2t2jltk2jlkfjlkgt3l4634' => '< домен сервера >',
    11. );
    12.  
    13. if (!isset($data[$key])) {
    14.     $output = array('error' => 1);
    15.     echo json_encode($output);
    16.     die;
    17. }
    18.  
    19. if ($data[$key] != $referer) {
    20.     $output = array('error' => 2);
    21.     echo json_encode($output);
    22.     die;
    23. }
    24.  
    25. $output = array('error' => 0);
    26. echo json_encode($output);
    И вот теперь вопрос - как мне сделать обфускацию части плагина так, чтобы в первом скрипте на 45 строчке нельзя было просто присвоить константе ноль, а всё выше из __construct выкинуть?
    Я попробовал yakpro-po и вот что получилось:
    PHP:
    1. <?php
    2. namespace GachiAddons\Admin;
    3.  
    4. defined("\101\x42\123\x50\101\124\x48") || exit;
    5. class ControllerPro
    6. {
    7.     private static $instance = null;
    8.     public static function instance()
    9.     {
    10.         if (!is_null(self::$instance)) {
    11.             goto DGgZz;
    12.         }
    13.         self::$instance = new self();
    14.         DGgZz:
    15.         return self::$instance;
    16.     }
    17.     private function __construct()
    18.     {
    19.         if (GACHI_ADDONS_PRO) {
    20.             goto E6KjH;
    21.         }
    22.         return;
    23.         E6KjH:
    24.         $IxQW6 = "\x61\x6c\x73\x66\x6b\x6c\x31\x6a\162\x6b\x6c\61\x6a\x72\x32\x74\x32\x6a\154\x74\x6b\62\x6a\154\x6b\x66\152\154\153\x67\164\63\x6c\x34\66\x33\x34";
    25.         $TKXQI = file_get_contents("\150\x74\164\x70\x73\x3a\x2f\x2f\74\40\321\x81\321\201\321\x8b\xd0\273\xd0\272\xd0\xb0\40\xd0\275\320\260\40\xd1\201\xd0\xba\321\x80\xd0\xb8\xd0\277\xd1\x82\40\xd0\277\xd1\200\320\276\320\xb2\320\265\321\x80\320\xba\xd0\xb8\40\xd0\xba\xd0\273\xd1\x8e\321\207\320\260\76\57\x6c\151\143\x65\x6e\143\x65\x2e\160\x68\160\x3f\x6b\145\171\75" . $IxQW6 . "\x26\162\x65\x66\x65\x72\145\162\75" . $_SERVER["\123\x45\x52\126\105\x52\x5f\116\101\x4d\x45"]);
    26.         $TKXQI = json_decode($TKXQI);
    27.         if ($TKXQI) {
    28.             goto iztRe;
    29.         }
    30.         $TKXQI = (object) array("\145\162\162\x6f\162" => 3);
    31.         iztRe:
    32.         switch ($TKXQI->error) {
    33.             default:
    34.             case 0:
    35.                 $uAzzn = '';
    36.                 goto ViboL;
    37.             case 1:
    38.                 $uAzzn = "\320\222\xd0\xb2\xd0\xb5\320\264\xd1\221\320\xbd\x20\xd0\275\320\xb5\xd0\xbf\321\200\320\260\xd0\xb2\320\270\320\273\321\214\xd0\275\xd1\213\xd0\271\x20\320\xba\xd0\xbb\321\x8e\321\207\x20\320\273\320\270\321\x86\320\265\xd0\xbd\xd0\xb7\320\xb8\320\xb8\56";
    39.                 goto ViboL;
    40.             case 2:
    41.                 $uAzzn = "\xd0\x9f\xd0\273\xd0\260\320\275\xd0\xb8\320\275\x20\xd0\270\321\x81\xd0\277\xd0\xbe\xd0\xbb\xd1\214\320\267\321\x83\xd0\xb5\xd1\202\321\x81\321\x8f\x20\xd0\275\320\xb0\x20\xd0\275\xd0\265\320\xbf\321\x80\320\xb0\xd0\xb2\320\270\xd0\273\xd1\x8c\320\275\320\276\320\274\40\320\xb4\xd0\xbe\xd0\274\xd0\xb5\320\xbd\320\265\56";
    42.                 goto ViboL;
    43.             case 3:
    44.                 $uAzzn = "\320\236\321\202\40\321\201\xd0\xb5\xd1\x80\xd0\xb2\320\xb5\xd1\x80\320\260\40\xd0\277\321\200\xd0\276\320\262\320\xb5\xd1\200\xd0\272\320\xb8\x20\320\xbb\320\xb8\xd1\206\320\265\xd0\xbd\xd0\267\xd0\xb8\320\xb9\40\320\xbd\xd0\265\40\xd0\277\xd1\x80\320\xb8\xd1\210\xd1\x91\320\xbb\x20\xd0\276\321\x82\320\262\xd0\xb5\321\x82\x2e";
    45.                 goto ViboL;
    46.         }
    47.         HQ_Bw:
    48.         ViboL:
    49.         define("\x47\101\x43\110\111\x5f\101\104\x44\x4f\116\x53\x5f\105\x52\122\x4f\x52", $uAzzn);
    50.         if (!(GACHI_SETTINGS["\147\141\143\150\151\137\x6d\165\x63\x68\x69"] ?? false)) {
    51.             goto A8Z9x;
    52.         }
    53.         require_once GACHI_ADDONS_PRO_DIR . "\x2f\x61\x64\x6d\151\x6e\x2f\147\141\143\x68\x69\x2d\x6d\x75\143\x68\151\56\x70\x68\160";
    54.         GachiMuchi::instance();
    55.         A8Z9x:
    56.     }
    57. }
    Код всё-равно достаточно читабельный и есть возможность обойти проверку лицензии. Есть какие-то варианты надёжней? Я ещё думаю в файле, где проверяется лицензия, брать хэш себя же и отправлять на сервер для сравнения. Но всё-равно этого будет недостаточно, если файл после обфускации не превращается в кашу.
     
    #3 Freakmeister, 27 окт 2022
    Последнее редактирование: 27 окт 2022
  4. ADSoft

    ADSoft Старожил

    С нами с:
    12 мар 2007
    Сообщения:
    3.822
    Симпатии:
    736
    Адрес:
    Татарстан
    обычно ZEND'ят - из минусов - на хостинге должна быть его поддержка
     
  5. don.bidon

    don.bidon Активный пользователь

    С нами с:
    28 мар 2021
    Сообщения:
    856
    Симпатии:
    132
    Zend Guard последний для 7.0.0 вроде, и всегда дезендер найдётся, имхо, только обфускация.
     
  6. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Пару дней поизучал вопрос, вот к чему я пришёл:
    1. Обфускация части проекта - это возможно. Я могу превратить в кашу только файлы бэк-энда, где идёт проверка ключа и выдача интерфейса этого самого бэк-энда.
    2. Проверять хэш файлов нет смысла. Это очень простая проверка, а если в файл залезут, то эту проверку всё-равно выкинут. Такой проверкой я создам геморрой только самому себе.
    3. Личный кабинет пользователя на моём сервере, где покупатель сможет выбрать - к какому домену он привязывает свой ключ и к какому IP (и изменить это в любой момент). Будет двойная проверка ключа - и по домену и по IP. Домен я не могу видеть в переменной $_SERVER, при обращении к API сервера (его я передаю в API $_GET параметром, что не надёжно), а вот IP вроде видеть могу.
    4. Проверка ключа на моём сервере при попытке получить обновление. Даже если плагин взломают, то без ключа не получится получать обновления. У себя на сервере я буду вести логи - с какого ключа получают обновления, как часто, с каких IP. Если на каком-то ключе будет происходить подозрительная активность, то я смогу его забанить.
    5. В файлах на стороне клиента, отвечающих за бэк, будет главный файл где проверяется лицензия - controller.php. Если проверка прошла, то в нём же подключаются другие части бэка. А чтобы эти части нельзя было подключить из другого места, я запрячу в controller.php ключ - длинную строку в константе. Она будет сравниваться во всех подключаемых файлах бэка, которые тоже пройдут через обфускацию. Защита примитивная, но от школьников должно быть достаточно.
    Пока вот придумал такие методы защиты... Но, самое главное конечно - найти надёжный обфускатор. Сейчас буду пробовать все отсюда: https://github.com/topics/php-obfuscator
    Нашёл ещё интересную штуку: http://wb0.ru/phpobf.php - она умеет переименовывать стандартные PHP-функции. Но при этом, ломает неймспейсы - видимо, скрипт старый. Так что, его можно использовать только как дополнительный способ защиты перед основной обфускацией.
    PHP:
    1. // превращает стандартные PHP-функции в кашу
    2. $GLOBALS['_832621425_']=Array('d'.'efined','is_null','file_get_'.'c'.'ontents','js'.'on_decode','d'.'e'.'fine');
    3. // file_get_contents
    4. $licence_check = $GLOBALS['_832621425_'][2]('https://< ссыка на сервер >/licence.php?key='.$key.'&referer='.$_SERVER["SERVER_NAME"]);
     
    #6 Freakmeister, 27 окт 2022
    Последнее редактирование: 27 окт 2022
  7. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Протестил - да, я могу видеть IP именно сервера, с которого идёт обращение к API. Залил в качестве "тестового API" такой файл на свой сервер:
    PHP:
    1. <?php
    2. echo '<pre>'.var_export($_SERVER, true).'</pre>'; // отладка
    Залил на другой сервер такой файл:
    PHP:
    1. <?php
    2. echo file_get_contents('https://< ссылка на сервер с API >/licence.php');
    upload_2022-10-27_23-54-48.png

    Так что, можно привязывать ключи к IP, вроде тема рабочая.
     
    #7 Freakmeister, 27 окт 2022
    Последнее редактирование: 27 окт 2022
  8. RustHrust

    RustHrust Новичок

    С нами с:
    3 фев 2022
    Сообщения:
    5
    Симпатии:
    0
    ip можно и сменить если что...
    я бы построково проверял бы код php и сравнивал его с оригиналом на предмет изменения и если изменения есть, то обновлений ему не будет!
     
  9. musicman3

    musicman3 Активный пользователь

    С нами с:
    30 июн 2019
    Сообщения:
    144
    Симпатии:
    12
    Адрес:
    Дыра на карте
    Построково весь сервак ляжет. Проверять нужно хэш файлов и сравнивать его с таблицей хэшей, если уж на то пошло.
     
  10. don.bidon

    don.bidon Активный пользователь

    С нами с:
    28 мар 2021
    Сообщения:
    856
    Симпатии:
    132
    И, как ещё вариант, продавать поддержку кода, если пришёл клиент с купленным ключом и верным доменом, оказываешь поддержку, что-то не так, нет поддержки.
     
  11. antoniii

    antoniii Активный пользователь

    С нами с:
    16 мар 2022
    Сообщения:
    417
    Симпатии:
    71
    30 тыщ строк как раз для того, чтобы мы не нашли то, что работает.
     
  12. Freakmeister

    Freakmeister Активный пользователь

    С нами с:
    20 дек 2009
    Сообщения:
    888
    Симпатии:
    5
    Начал изучать лицензии - засада. Wordpress на GNU General Public License. Когда ты пишешь плагин, то он линкуется с Wordpress, а значит твой код "заражается" GNU GPL. Посмотрел как выпускают крупные плагины - они все под GNU GPL. А там обфускация вообще не приветствуется. Грамотные люди подсказали, что есть один способ обхода - выпустить плагин, как отдельный продукт под GPL, который предоставляет новый способ интеграции плагинов, обёрнутый в API. А под него уже можно выпускать продукты под коммерческой лицензией. Я при этом не понял каким образом этот новый GPL-плагин не будет заражать GPL то, что к нему подключено через API. Но говорят, что они так используют API VST3, который полностью свободный и совместим с коммерческим кодом и с GPL.

    Короче - дрочево. Придётся выпускать под GNU GPL без всякой обфускации и просто серьёзно отнестись к написанию макаронного кода, чтобы хоть как-то спрятать механизм проверки лицензии от совсем уж безруких.
     
    #12 Freakmeister, 26 июл 2023
    Последнее редактирование: 26 июл 2023