{{LiveInternetCount: empty}}
Джино: хостинг и веб-сервисы

Система Orphus
{{LiveInternetLogo: empty}}
Russian version
Добавить на Del.icio.us
English version
Добавить на Digg.com

 dkLab | Конструктор | HTTP_UrlSigner: безопасная передача параметров в URL и его цифровое подписывание 

Карта сайта :: Форум «Лаборатории» :: Проект «Денвер»
Проект «Orphus» :: Куроводство: наблы :: Конструктор


2009-12-18
Обсудить на форуме

Принять участие в разработке библиотеки/утилиты можно на GitHub.

HTTP_UrlSigner позволяет динамически строить URL, защищенный от подделки злоумышленниками. Имея такой URL, вы можете убедиться, что он составлен именно вашей системой, а не кем-то еще, и извлечь из него ранее записанные параметры, не опасаясь, что их могут подменить.

Пример использования HTTP_UrlSigner

Листинг 1: Пример использования: формирование URL
$signer = new HTTP_UrlSigner("very-secret-word", "http://slave.com/page/*?xyz");
echo $signer->buildUrl(array("a" => 123, "b" => array("x" => 1, "y" => 2)));
// Результат:
// http://slave.com/page/af0b386b9dc43dc0/a879fde2e01643fa1/estMsTU0MDA0wMVMrBwmqZ?xyz

Листинг 2: Пример использования: извлечение параметров из URL
$signer = new HTTP_UrlSigner("very-secret-word", "http://slave.com/page/*?xyz");

print_r($signer->parseUrl($_SERVER['REQUEST_URI']));
// Результат:
// Array (
//   [a] => 123
//   [b] => Array (
//     [x] => 1
//     [y] => 2
//   )
// )
// либо, если URL подделан, генерируется исключение

print_r($signer->parseUrl("http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"));
// Результат: такой же. 
// Однако на этот раз проверяется полное совпадение URL, включая имя хоста.

Зачем нужно цифровое подписывание?

Предположим, в составе вашего сайт имеются два домена: main.com и slave.com. Находясь на main.com, вы хотите передать на страницу сайта slave.com параметры array("name" => "Pupkin", "phone" => "1234567"). Конечно, это можно сделать так:

Листинг 3: Небезопасная передача параметров с main.com на slave.com
http://slave.com/page.php?name=Pupkin&phone=1234567

Однако злоумышленник может вручную сформировать этот URL и подменить в нем телефон на другой. В результате скрипт page.php на slave.com получит неверные данные.

Чтобы защититься от подделок, придумали алгоритм под названием "цифровое подписывание". Он работает так: к передаваемым данным добавляется специальный хэш, цифровая подпись:

Листинг 4: Пример URL с цифровой подписью
http://slave.com/page.php?name=Pupkin&phone=1234567&sign=d41d8cd98f00b204e9800998ecf8427e

где значение параметра sign вычисляется по схеме:

Листинг 5: Пример URL с цифровой подписью
$signInUrl = md5($SECRET . serialize($params));

Параметр $SECRET необходимо хранить в секрете; его должны знать только сайты main.com и slave.com, но никто больше. Теперь скрипт page.php на сайте slave.com извлекает данные $params и проводит точно такую же операцию на своей стороне:

Листинг 6: Вычисление цифровой подписи на принимающей стороне
$calculatedSign = md5($SECRET . serialize($params));

Что это нам дало? Если значения переменных $calculatedSign и $signInUrl совпали, значит, URL сформирован нами, а не злоумышленниками (ведь злоумышленник не знает значения $SECRET).

Особенность HTTP_UrlSigner

Взглянем еще раз на пример URL, который формирует HTTP_UrlSigner:

Листинг 7: Пример URL, сформированного HTTP_UrlSigner
http://slave.com/page/af0b386b9dc43dc0/a879fde2e01643fa1/estMsTU0MDA0wMVMrBwmqZ?xyz

Мы видим, что HTTP_UrlSigner не просто добавляет цифровую подпись в виде GET-параметра к уже имеющемуся URL. Его задача шире:

  1. Обеспечить возможность передачи параметров без задействования QUERY_STRING; дать возможность указать шаблон построения результирующего URL, где вместо "*" вставляются подписанные данные.
  2. Гарантировать неизменность не только параметров, но всего сформированного URL, с точностью до символа.

Передача данных без QUERY_STRING

Параметры (произвольный ассоциативный массив) должны упаковываться и при необходимости вставляться в середину URL, а не только в QUERY_STRING. Почему это важно? Дело в том, что подписанные URL часто используют для доступа к медиа-контенту (картинки, JavaScript- и CSS-файлы и т. д.). Для них очень важно правильное браузерное кэширование. Однако некоторые старые браузеры или слишком "умные" прокси-серверы плохо относятся к URL, содержащим непустой QUERY_STRING. Поэтому мы должны иметь полную свободу действия.

При формировании подписанных данных по возможности применяется gzip-сжатие; в ряде случаев (если параметров много) это серьезно сокращает длину URL. Кроме того, библиотека следит за тем, чтобы в результирующем URL был как минимум один "/" на каждые 80 символов; это необходимо для IE6, который неверно ведет себя при кэшировании "файлов" (как он думает) с длинными (по его мнению) именами.

Подписывание URL с точностью до байта

HTTP_UrlSigner подписывает не параметры, а весь URL целиком (при необходимости — включая имя хоста и протокола). Таким образом, злоумышленник не сможет даже добавить незначащих параметров в конец URL: он сразу же окажется невалидным. Например, URL

http://slave.com/page/af0b386b9dc43dc0/a879fde2e01643fa1/estMsTU0MDA0wMVMrBwmqZ?xyz

пройдет проверку цифровой подписи, но если к нему добавить "&abc=1":

http://slave.com/page/af0b386b9dc43dc0/a879fde2e01643fa1/estMsTU0MDA0wMVMrBwmqZ?xyz&abc=1

то он уже будет некорректным. Зачем же это нужно?

Нужно это для защиты от злонамеренного "забивания" кэша reverse-proxy (если он используется), а также для защиты от DoS-атак.

Хорошая иллюстрация тут — кэш nginx, о котором я писал в статье Подводные камни при использовании кэширования в nginx. Если ключ кэширования выбирается равным REQUEST_URI, то злоумышленник, добавляя в REQUEST_URI незначащие параметры, может организовать нам серию "кэш-промахов", перегрузив тем самым сервер.

HTTP_UrlSigner не дает этого сделать: любой URL, отличающийся от эталонного, будет распознан как поддельный и не даст перегрузить сервер или забить кэш nginx.

Резюме

На базе HTTP_UrlSigner вы можете строить инструменты, требующие генерации URL, защищенных от подделки. Вероятнее всего, эти инструменты также будут требовать интенсивного серверного кэширования, которое удобно организовывать при помощи nginx.

Вот несколько примеров практического применения техники подписывания URL:

  • Средство для объединения нескольких CSS- и JS-файлов в один большой "на лету".
  • Автоматический ресайз картинок "на лету" (здесь кэширование просто обязательно!).
  • Защищенная передача данных между доменами в случае, если организация кросс-доменной сессии представляется сложной.






Дмитрий Котеров, Лаборатория dk. ©1999-2016
GZip
Добавить на Del.icio.us   Добавить на Digg.com   Добавить на reddit.com
{{Counters: empty}}