Во втором посте этой серии я опишу методы проверки целостности удаленного кода — от контрольных сумм до (простой) инфраструктуры открытого ключа. Для передачи кода я представляю популярные Phar архивы.
Контрольные суммы
В
первом посте я говорил о растущей необходимости переноса и выполнения кода из удаленных мест. Я также описал, почему слепое доверие к загруженному коду является уязвимостью безопасности, даже если мы загружаем из известного места под нашим контролем.
Чтобы быть уверенным, что загруженный код не был изменен (злоумышленником), мы должны как-то проверить его. Самая простая форма проверки — сравнить контрольную сумму кода с известным значением. Давайте посмотрим на пример:
// checksum validation $url = 'http://code.example.com/code.php'; $file = file_get_contents($url); if (md5($file) == $code_checksum) { file_put_contents('code.php', $file); inlcude 'code.php'; }
Мы загружаем код по беспроводной сети, но перед его выполнением мы вычисляем сумму MD5 содержимого файла и сравниваем ее с нашим значением. Контрольная сумма легко обнаруживает ошибки при передаче файла. Кроме того, если злоумышленник предоставит свой код, контрольная сумма будет другой (давайте на мгновение проигнорируем
коллизии хеша ), и код не будет выполнен. В чем проблема? Есть два:
- Как мы должны получить контрольные суммы в первую очередь? Если они у нас есть локально — какой смысл вообще не отправлять код с ними (за исключением вопросов лицензирования)? Если мы этого не сделаем — нам нужно получить их, чтобы они могли также быть подделаны.
- Контрольные суммы привязаны к конкретной версии кода — если мы обновляем удаленный код, нам необходимо предоставить новые контрольные суммы всем клиентам (и сделать это безопасно).
Описанные проблемы — это то, что известно в криптографии как классическая
дилемма обмена ключами .
Подписание кода
К счастью, если мы заранее знаем, кто будет отправлять код в наши приложения, эти проблемы будут решены с помощью пары открытый / закрытый ключ и
подписи кода . Давайте посмотрим, как работает подписывание кода:
Идея проста — производитель (автор кода) генерирует два ключа —
открытый и
закрытый . Ключи работают вместе, как две стороны монеты. Если один ключ изменяется, математические отношения между ними нарушаются. При создании программного обеспечения производитель использует свой
закрытый ключ и сам код для генерации подписи кода — особая форма контрольной суммы. Затем контрольная сумма вместе с кодом объединяется в пакет «подписанный код» (например, ZIP-файл).
При проверке кода используется не закрытый ключ, а второй ключ из пары — открытый. Потребитель проверяет подпись пакета и проверяет ее, используя
открытый ключ производителя. Если кто-то изменяет код в пакете — подпись не будет совпадать (это функциональность контрольной суммы). Но даже если пакет имеет правильную контрольную сумму,
но подписан другим закрытым ключом — его можно обнаружить (потому что открытый и закрытый ключи связаны!). В результате, если у злоумышленника не будет закрытого ключа производителя, он не сможет подделать код, поскольку он не пройдет проверку подписи.
Как использовать это в нашем примере?
- Сгенерировать закрытый и открытый ключ для производителя (это также может быть сторонний сервер)
- Отправьте открытый ключ производителя вместе с клиентской частью приложения.
- Всякий раз, когда код для загрузки изменяется, подписать его на производителя
- Клиент загружает новый код и проверяет его перед выполнением (не загружайте открытый ключ снова! — он может быть подделан)
Даже если злоумышленник выполняет атаку «человек посередине» и отправляет код вместо сервера-производителя, не зная секретного ключа (который надежно хранится на сервере-производителе и никогда не передается), его код никогда не будет выполнен. Мы выполняем только тот код, который подписан закрытым ключом в паре с нашим известным локальным открытым ключом.
Подписание кода в мире PHP — Phar
Возвращаясь к PHP, мы должны решить две проблемы:
- Какой формат пакета использовать? Мы не можем просто добавить подпись после кода, так как для этого потребуется удалить ее вручную перед проверкой.
- Как выполнить фактический процесс проверки / подписания?
К счастью, есть
Phar, и он прекрасно решает обе проблемы. Phar — это контейнерный формат (например,
.jar для Java) для приложений PHP, который также позволяет подписывать и проверять подписи, используя
пару открытых / закрытых ключей OpenSSL .
Установка Фар
Чтобы использовать Phar для подписи кода, вам нужно скомпилировать PHP с
ключом —with-openssl . Для PHP <5.3 вам также необходимо собрать расширение Phar (2.0.0+) из
http://pecl.php.net .
Например, в Ubuntu эти шаги необходимы для сборки и настройки расширения:
$ sudo apt-get install php5-dev $ sudo pecl install pecl/phar $ echo "extension=phar.so" | sudo tee /etc/php5/conf.d/phar.ini $ echo "phar.readonly=0" | sudo tee -a /etc/php5/conf.d/phar.ini
Последняя строка позволяет нам создавать архивы Phar, так как по умолчанию поддерживается только их чтение.
Создание ключей
Вам также необходимо сгенерировать пару открытый / закрытый ключ — используйте следующие команды OpenSSL:
#!/bin/bash # priv.pem will contain your private key # pub.pem will contain your public key openssl genrsa -out priv.pem 1024 openssl rsa -in priv.pem -pubout -out pub.pem
Подписание с Phar
Подписание очень просто (кроме ошибок в документации :()
$src = './src'; // source files to be built $dest = './build/test.phar'; // phar destination file $priv_file = './cert/priv.pem'; // path to PEM private file $pub_file = './cert/pub.pem'; // path to PEM public file $phar = new Phar($dest); // get the private key $private_key = file_get_contents($priv_file); // apply the signature $phar->setSignatureAlgorithm(Phar::OPENSSL, $private_key); $phar->buildFromDirectory($src); // attach the public key for verification copy($pub_file, $dest . '.pubkey');
Phar требует, чтобы открытый ключ хранился
вместе с архивом phar в жестко запрограммированном месте, поэтому последняя строка копирует наш файл открытого ключа рядом с ним.
Проверка подписи в Phar
Чтобы использовать Phar архивы локально, вы просто делаете:
require_once '/path/to/test.phar';
Это извлечет соответствующий открытый ключ и выполнит проверку, выдав исключение по ошибке.
Если вам нужно использовать архив Phar удаленно, вам не повезло. Из соображений безопасности
Phar не будет работать с удаленными URI (другими словами, вы не можете удаленно включать архив Phar). Также
открытый ключ должен храниться рядом с архивом в заранее определенном месте. Хотя Phar решает проблему подписи, он делает это только для локальных включений.
Итак, возвращаясь к вопросу — как безопасно включить удаленный код? Нам нужно найти способ
удаленной доставки архивов .phar И проверить подписи без передачи ключа, о чем пойдет речь в
последнем посте из этой серии .