Защита дополнений в MODx шифрованием

Для того чтобы зашифровать данные в пакете давайте сперва изучим как он устроен. В данной статье приведу пример как защитить пакет на примере механизма защиты введенного на сайте-магазине MODStore.PRO. Пример исходных материалов для сборки дополнения вы можете посмотреть здесь на GitLAB.

В механизме сборки распространяемых пакетов (transport) заложена очень гибкая механика. Первичная информация о пакете содержится в манифесте - manifest.php. В нем описаны характеристики пакета-транспорта и перечисляется список носителей - vehicles. В частности именно содержимое vehicle и подлежит шифрованию.

В MODx есть предопределенный набор видов носителей:

  • xPDOObjectVehicle - основной используемый класс, в нем хранится описание объектов
  • xPDOFileVehicle - содержит описание о копировании файла
  • xPDOScriptVehicle - содержит описание о запуске скрипта PHP

У каждого vehicle может быть свой набор обработчиков проверки (validator) и разрешителей (resolver). Они в свою очередь могут быть типов: file, php. Для решение нашей задачи понадобится resolver обоих типов. 

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

Нам понадобится класс хранителя на основе xPDOObjectVehicle, назовем его EncryptedVehicle.

Класс EncryptVehicle выполняет несколько задач:

  • Обработка упаковки носителя
  • Обработка дешифровки носителя
  • Обход дерева каталога для шифрования
  • Обход дерева каталога для расшифрования
  • Получение ключа шифрования

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

Получается следующий порядок носителей и обработчиков:

  • xPDOFileVehicle с файлом-классом EncryptedVehicle. Однако он не будет скопирован/установлен за пределы распакованного транспорта. Обращение будет происходить напрямую к нему. Это позволит избежать проблем с удалением, т.к. файлы/каталоги будут пересекаться с теми, которые подлежат удалению.
  • xPDOScriptVehicle загрузчик класса EncryptedVehicle и xPDOObjectFile (он понадобится для загрузки при удалении транспорта)
  • xPDOObjectVehicle с основным хранилищем объектов и резолвер с файлами. Так же сюда добавим свой резолвер resolve.decode.php для дешифровки файлов после установки. Он нужен здесь, т.к. резолвер обрабатывает данные после установки носителя, а до этого файлы еще не будут установлены в систему.
  • ...
  • xPDOScriptVehicle загрузчик класса EncryptedVehicle и xPDOObjectFile

 

Теперь что нужно и куда добавить.

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

Переменные MODSTORE_LOGIN и MODSTORE_KEY определите в конфигураторе.

$keyRequester = new KeyRequester($modx, [
 KeyRequester::PARAM_USERNAME => MODSTORE_LOGIN,
 KeyRequester::PARAM_API_KEY => MODSTORE_KEY, // modstore.pro API
 KeyRequester::PARAM_PACKAGE => PKG_NAME_LOWER,
 KeyRequester::PARAM_VERSION => PKG_VERSION . '-' . PKG_RELEASE
]);

define('PKG_ENCODE_KEY', $keyRequester->getKey());

Добавим носитель с классом

$builder->package->put(new xPDOFileVehicle, [
 'vehicle_class' => xPDOFileVehicle::class,
 'object' => [
  'source' => $sources['build'] . 'helpers/encryptedvehicle.class.php',
 ]
]);

Следом добавляем загрузчик класса

$builder->package->put(new xPDOScriptVehicle, [
 'vehicle_class' => xPDOScriptVehicle::class,
 'object' => [
  'source' => $sources['build'] . 'helpers/encryption.php'
 ]
]);

Затем собираем основной набор объектов и в объекте категории указываем, что класс обработчика будет EncryptedVehicle.

$category = $modx->newObject('modCategory');
$category->set('category', PKG_NAME);
/* create category vehicle */
$attr = array(
 xPDOTransport::UNIQUE_KEY => 'category',
 xPDOTransport::PRESERVE_KEYS => false,
 xPDOTransport::UPDATE_OBJECT => true,
 xPDOTransport::RELATED_OBJECTS => true,
 'vehicle_class' => EncryptedVehicle::class
);

...

$vehicle = $builder->createVehicle($category, $attr);

/* now pack in resolvers */
$vehicle->resolve('file', array(
 'source' => $sources['source_assets'],
 'target' => "return MODX_ASSETS_PATH . 'components/';",
));

$vehicle->resolve('file', array(
 'source' => $sources['source_core'],
 'target' => "return MODX_CORE_PATH . 'components/';",
));

...

$builder->putVehicle($vehicle);

Добавляете нужные носители и в конце еще раз загрузчик класса.

$builder->package->put(new xPDOScriptVehicle, [
 'vehicle_class' => xPDOScriptVehicle::class,
 'object' => [
  'source' => $sources['build'] . 'helpers/encryption.php'
 ]
]);

Это что касается сборщика транспорта.

Загрузчик класса helpers/encryption.php

<?php
/**
* @var xPDOTransport $transport
*/

if (!class_exists('EncryptedVehicle')) {
 $classIndex = 0;

 if (!class_exists('xPDOObjectVehicle'))
  $transport->xpdo->loadClass('transport.xPDOObjectVehicle', XPDO_CORE_PATH, true, true);

 // Store info about encoder in first
 $payload = include($transport->path . $transport->signature . '/' . $transport->vehicles[$classIndex]['filename']);
 $path = $transport->path . $transport->signature . '/' . $payload['class'] . '/' . $payload['signature'] . '/';
 $transport->xpdo->loadClass('EncryptedVehicle', $path, true, true);
}

return true;

Этих изменений достаточно для рас/шифрования внутренней информации внутри vehicle. Чтобы расшифровать файлы нужно их обработать после их установки в систему через обработчик resolver. Он должен быть перечислен после файлового резолвера, который производит копирование файлов в систему.

Резолвер resolve.decode.php надо прописать в конфигуратор и разместить файл в каталог resolvers.

<?php

if ($object->xpdo) {
 /** @var modX $modx */
 $modx =& $object->xpdo;

 switch ($options[xPDOTransport::PACKAGE_ACTION]) {
  case xPDOTransport::ACTION_INSTALL:
  case xPDOTransport::ACTION_UPGRADE:
   if ($options['vehicle_class'] == 'EncryptedVehicle') {
    foreach ($options['resolve'] as $idx => $values) {
     if ($values['type'] == 'file') {
      $fileMeta = $transport->xpdo->fromJSON($values['body'], true);
      $fileTarget = eval($fileMeta['target']);
      $fileTargetPath = $fileTarget . $fileMeta['name'];
      EncryptedVehicle::decodeTree($fileTargetPath);
      $modx->log(xPDO::LOG_LEVEL_DEBUG, "Contents decoded: $fileTargetPath");
     }
    }
   }

   break;

   case xPDOTransport::ACTION_UNINSTALL:
    break;
 }
}

return true;


Метки: modxразработка

Просмотров:
Опубликовано: 25.09.2018