Использование Nginx и php для проверки прав доступа перед отдачей файлов

    Иногда на сайте возникает необходимость ограничить доступ к некоторым файлам по разным причинам(раздавать файлы только авторизованным пользователям, антилич и так далее). Для решения данной задачи можно использовать разные подходы:

    1. Раздавать файлы с помощью скрипта на php(заменить php на то, что нравится больше). При данном подходе мы передаем в качестве параметра скрипту имя файла. Код проверяет все условия, при которых возможно получения доступа к данному файлу и принимает решение, выдать 404 или запрошенный файл. Данный подход подходит для мелких файлов, однако при возрастании размера отдаваемого файла он будет потреблять много системных ресурсов, т.к. файл будет вычитываться в память и затем отдаваться.
    2. Использовать некоторые неочевидные возможности веб-серверов.


    Рассмотрим второй вариант.
    Так исторически сложилось, что для обратного проксирования я стал использовать nginx. Покопавшись в его документации я обнаружил что с помощью данного сервера можно контролировать раздачу файлов, проверяя права доступа непосредственно перед отдачей контента.
    Итак, приступим.
    За nginx у меня стоит апач, который обрабатывает запросы на динамический контент, описано примерно так:
    location / {
    proxy_pass 127.0.0.1/;
    }


    Сначала складываем наш контент в выделенные директории. В моем случае сайт расположен в /var/www, защищенный контент я выложил в /var/www/protected. Для данной секции я добавил в конфигурации nginx следующие строки:
    location /protected {
    root /var/www;
    internal;
    }


    Здесь root указывает, где находится сайт. Директива internal указывает что данная область будет доступна только если происходит внутреннее перенаправление nginx в директорию protected. Таким образом, даже зная прямой адрес ресурса на сервере, мы будем получать 404 в ответ на наш запрос.
    Первый этап завершен, контент не доступен по прямой ссылке.
    Однако нам нужно при некоторых условиях данный контент все же показывать. Для этого мы приводим первый location к следующему виду:
    location / {
    rewrite ^/download/(.*) /download.php?path=$1 last;

    proxy_pass 127.0.0.1/;
    }

    Таким образом, все запросы, которые пытаются забрать что-то из download, будут перенаправляться на файл download.php. В данном файле будет приниматься решение о разрешении/запрещении доступа пользователю к файлу. Сам исходный текст файла download.php может быть таким:
    <?
    $path = $_GET[«path»];
    //некоторые действия по проверке прав доступа
    header(«X-Accel-Redirect: /protected/». $path);
    ?>

    Если пользователю разрешен доступ к данному файлу, тогда отправляем данный заголовок, в ином случае отправляем ему 404. После определения прав доступа работа php завершена. Далее nginx получает данный заголовок, выполняет внутреннее перенаправление и начинает отдавать пользователю запрошенный файл.

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

    Основой для статьи полужил материал: blog.kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/lang/ru
    Использовались материалы: sysoev.ru/nginx/docs

    P.S. Данная статья не претендует на полноту и не может рассматриваться как инструкция по настройке сервера nginx. В ней сознательно опущены моменты, касательно сжатия, кэширования и тому подобных вещей.
    Поделиться публикацией

    Похожие публикации

    Комментарии 12

      +1
      возможно это и подойдет для частного случая… Но не подойдет для тех кто пользуется хостингом, а в основном именно у них с этим проблеммы (
        0
        Согласен. Данное решение подходит только в случае, когда мы имеем хоть какой-то доступ к конфигурации сервера, либо напрямую(тогда это наш сервер), либо опосредованно через администратора саппорта. В других случаях приходится этот материал бесполезен.
        0
        давно и успешно используем этот подход у себя на проекте.
          +5
          Ещё есть заголовок в ту же тему X-Accel-Limit-Rate: для орагичения скорости.

          А также полезно указывать при отдаче «Content-Disposition: attachment; filename=\»$native_name\«»; для того чтобы файл скачивался, а не открывался.

          А если написать вот так header(«Content-Type:»); то nginx сам будет mime тип подставлять.
            0
            кто-то рассказал есть ли возможность сделать ограничение на количество отдаваемого, типа для незарегистрированных проигрываем только демо 30 секунд. А то на каждую песенку приходится создавать отдельные файлики с демо…
              0
              кто бы
                0
                Основываясь на этом методе на одном из сайтов мы накладывали водяные знаки на картинки, если у пользователя не хватало прав получить ее версию без водяных знаков. При этом при обращении к картинке проверялось наличие уже сгенерированной версии с водяным знаком, если такая версия уже была, ее отдавали, если нет, генерировали, клали на файловую систему и отдавали пользователю.
                Если будет найден способ быстро генерировать демо при первом обращении, то такой способ может вполне прокатить. Однако, как мне кажется, преобразование картинок несколько отличается от преобразования песенок.
                  0
                  Ничем, вобщем-то, не отличается. А в предложенном способе не хватает удаления сгенеренного контента, к которому не было обращений н-ное время. Иначе место на сервере будет расходоваться нерационально.
                0
                использую на своём сайте этот метод уже очень давно, так как сижу на отдельном сервере. Так же нарыл эту возможность где-то на опеннете, и настроил в течении 10 минут. Основной плюс метода, что контент отдаётся нгинксом напрямую без апача, поддерживая докачку и разбиение на части без проблем. Для меня это было просто идеально, и я очень рад что обнаружил и задействовал сей метод.
                  0
                  Просто спасибо! :)
                    0
                    Пытался применить этот прием для проверки авторизации на фронтенде. Нгинкс переписывал запрос на авторизационный скрипт на бекенде, который в случае успеха проверки, ставил заголовок X-Accel-Redirect. Только не на статический файл, а на локейшн того же нгинкса, и вообще там был запрос. В локейшне защищенном запрос мог быть вторично переписан на статический файл в кеше, либо проксирован бекенду.

                    Так вот, когда X-Accel-Redirect делался не на статику, а на скрипт с параметрами, то нифига он не отрабатывался. Такие вот дела…
                      0
                      А как посчитать отданный трафик?

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

                      Самое читаемое