8 июня 2011 в 17:18

Недокументированные возможности secure_link

  Начиная с версии 0.8.50 в модуле Nginx secure_link появилось значительное улучшение по защите ссылок от подделывания. Как ни странно, новые возможности до сих пор не отражены в официальной документации [1]. Описание на английском можно найти на сайте Nginx Community [2], а также пояснения Сысоева по данным изменениям [3]. Код на PHP для генерации защищённых ссылок можно найти в форуме по nginx [4].
 В общем, новшество выглядит так :

nginx.conf:
location ~ ^/p/(?<secure>[\w-]+,\d+)/(?<secured_stuff>.+)$ {
     secure_link     $secure; 
     # в данном примере '$secure' = "HASH,TIMESTAMP"
     secure_link_md5      PASSWORD$secure_link_expires$secured_stuff;
     # '$secure_link_expires' это переменная nginx содержащая значение после запятой из 'secure_link' = TIMESTAMP

     if ($secure_link = "") { return 403; } # invalid link
     if ($secure_link == 0) { return 410; } # expired link
     
     # link is ok, do something here
}


URL может выглядеть так: /p/HASH,TIMESTAMP/ANYTHING где
 PASSWORD секретный пароль
 TIMESTAMP это время до которого действительна ссылка в unix epoch
 ANYTHING произволный текст
 HASH кодированный в base64-URL [5] md5 хэш из шаблона secure_link_md5 ($secured_stuff = ANYTHING в данном примере). Символы '=' после base64 можно опускать

  Nginx имеет пару неявных нюансов в подсчете md5 суммы:
1. строка для подсчета из URL ($secured_stuff) декодируется из URL encoding в оригинальный вид
2. md5 хэш должен передаваться в бинарном виде для кодирования в base64

  код для PHP может быть такой:
$time  = time() + EXPIRE_TTL; # = TIMESTAMP
$hash = md5(PASSWORD.$time.$secured_stuff, true);
$hash = strtr( base64_encode($hash), array( '+' => '-', '/' => '_', '=' => '' ));
$url    = 'http://example.tld/p/$hash.','.$time.'/'.$secured_stuff;


Для передачи хэша и времени можно также использовать аргументы и куки:
для URL вида
http://example.com/p/files/top_secret.pdf?st=PIrEk4JX5gJPTGmvqJG41g&e=1324527723

где агрумент st это хэш от PASSWORD, URI и аргумента e
location вида
 
location /p/ {
     secure_link $arg_st,$arg_e; # this must match the URI part related 
     secure_link_md5 PASSWORD$uri$arg_e; # PASSWORD is the secret token
     ....
}

Как не сложно догадаться, при формировании хэша можно использовать IP адрес клиента.

Ссылки:
[1] sysoev.ru/nginx/docs/http/ngx_http_secure_link_module.html
[2] wiki.nginx.org/HttpSecureLinkModule
[3] nginx.org/pipermail/nginx/2010-September/022324.html
[4] forum.nginx.org/read.php?21,126363,128324#msg-128324
[5] en.wikipedia.org/wiki/Base64#URL_applications
Deniss @slimlv
карма
9,0
рейтинг 0,0

Комментарии (30)

  • +8
    Простите за мою тупость, но что такое поддельная ссылка и зачем от них защищаться?
    • 0
      Наверное, типа фигни как «для восстановления пароля».
    • 0
      бывает важно чтобы ты знал что это твоя ссылка,
      например защита от встраивания на чужой сайт.
    • 0
      токены?
      подтверждение мыла, восстановление пароля, еще куча вещей.
      • +2
        А не проще это сделать на стороне приложения? Я всетаки думаю, что это для защиты файлов от скачивания, или что-то подобное.
    • +9
      основное назначение — запрещение прямых ссылок на файлы с чужих сайтов.
      Типа зашел на сайт — можешь скачать файл; скопировал прямую ссылку и послал другу — у него не работает.
      • 0
        А разве нельзя в запрос приписать реферер с корнем сайта или что-то вроде?
        • +1
          это как?
    • +2
      В первую очередь для того, чтобы быть уверенными, что ссылка сформирована именно вашим приложением.
      Переходы по «левым» ссылкам используются для того, чтобы перегрузить бэкэнд или забить кеш nginx.
      Если ссылка невалидная, то запрос до бэкэнда просто не дойдет.
      Также уменьшается риск от sql-injection (так как злоумышленник не имеет возможности изменить параметры).

      На PHP есть отличная библиотека HTTP_UrlSigner от Дмитрия Котерова: dklab.ru/lib/HTTP_UrlSigner/
      • –1
        Вы всерьез предлагаете пихать в каждый url кусок непонятной фигни, вместо нормальной работы с базой? А как быть с поисковиками? Кто-то узнает ваш секретный ключ, вы поменяете его в целях безопасности и сайт моментально выпадет из индекса.
        • +1
          Нормальную работу с базой никто не отменял. Но даже профессионалы допускают ошибки, а в случае же такого дополнительного эшелона обороны эксплуатации уязвимости будет существенно затруднена.
          Если узнали ваш секретный ключ — значит злоумышленники получили доступ к исходному коду приложения на вашем хосте, что говорит о серьезных проблемах безопасности.
          Схема подписи url достаточно широко распространена и общепринята, посмотрите хоть на Яндекс Маркет.
          • –1
            > Если узнали ваш секретный ключ — значит злоумышленники получили доступ
            > к исходному коду приложения на вашем хосте, что говорит о серьезных
            > проблемах безопасности.
            Ну говорит, и? Взломали вас, узнали ключ, вы закрыли дыру, что дальше? Меняете ключ — сайт вылетает из индекса. Не меняете — ваша защита рушится.

            > посмотрите хоть на Яндекс Маркет.
            Посмотрел, не увидел.
            • –1
              Посмотрите на то, как выглядят ссылки на товары (по которым осуществляется переход на страницы магазинов)

              http://market-click2.yandex.ru/redir/GAkkM7lQwz6kEtXSvIPZ1pBp6WPs4XQLa-BxMEn9Wi4Bcs3zInCad3l6IwJap6nZmbF_pTg6o5q5u9HnQapByJnk19tG6aXswQa2AOxrkzBiuktpwqeI5_ot_JD49Iq5Zvzl-9hnFeSqILc2z4-wuzv37HjFfdHag3Qj7RKx2kpJgWQ-Ozt16nFqS0F5ntjRIguIIWg_yNBnVrNTuEV83pU8WsVCy0R5f4LX2RD9F_1Ob9z7pZkfjX0Q3oR1weVT?data=QVyKqSPyGQwwaFPWqjjgNvZTe7It9MxftkcZYMdf94rx-Mc_xE6i_IERl2JCZl06qnFhBgkwoYnORHLAvnQ8VHmVVc5Cmu3Oz9lQ9_oBY-J5uTAu4ebBTpy8flRGfKa5&b64e=2&sign=180d1d0b4ee9a438e4dcc058595dc7de
              &keyno=1


              Попытка измения любого из параметров приведет к редиректу на главную яндекса.
              • –2
                Ну вы же сами показали, что не правы. Не для уменьшения нагрузки на бекенд, ни для предотвращения забивания кеша, не для защиты от инъекций это не используется.
              • 0
                это от накрутчиков
  • 0
    спасибо, хорошая статья, как раз начинаем прикрычивать этот модуль.
  • +2
    /p/HASH,TIMESTAMP/ANYTHING где
    PASSWORD секретный пароль

    s/p/PASSWORD/?
  • +6
    «оффициальной»… Хочется рыдать…
    • 0
      Да ладно вам. У нас вот президент проффесор, и ничего, как-то он не особо беспокоится ;)
    • 0
      ээээ… не плачте — исправил!
  • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    С начала марта этого года использую подобное решение. Вообще, статья — перепечатка исходника по ссылке №2. — все тоже самое, только на английском. И это, кстати, вполне себе официальная документация.
    • 0
      «The information provided on this wiki is the work of volunteers.»
      wikipedia официальная документация по сборке холодильника «Беларусь»?
      • +2
        Linux is the work of volunteers
  • 0
    это сделано для того что бы напримено нельзя было простым перебором скачать файл, зная его имя.

    К.О
  • 0
    содержащая значение после запятой из 'secure_link' = TIMESPAMP


    %s/TIMESPAMP/TIMESTAMP/g
  • 0
    а ничего, что в версии
    0.8.51 27.09.2010
    Изменение: директива secure_link_expires упразднена.
    • 0
      ничего
  • 0
    У меня был сделан патч, чтобы можно было использовать пустую ссылку, т.е. чтобы просто считать md5 от параметров из GET или Cookie и не указывать после хэша вообще ничего, ну в крайнем случае просто /secure/hash/?arguments…
    Хотел написать статью но карма не позволяет :)
    • 0
      эээ… а что потом сверять с хэшем?
      работа с args и cookies уже реализованна в стандартном secure_link, читайте документацию внимательней

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