Контроль доступа к файлам с использованием Apache mod_rewrite и эмуляции сессий в PHP.

О том, как сделать доступ к большим файлам только после авторизации посетителя на сайте.

Введение.

Для контролируемого доступа посетителей на сайт обычно используют авторизацию при помощи PHP, с использованием сессий, или Web-сервера Apache, с использованием .htpasswd.

Авторизация средствами PHP позволяет гибко управлять правами доступа пользователей и их учётными записями.

PHP авторизация.

Когда посетитель пытается зайти на страницу, интерпретатор PHP проводит авторизацию и по её результатам решает выводить или не выводить информацию

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

Скрипт должен быть прослойкой между файлом и посетителем, т.к. если у посетителя будет прямая ссылка на файл, то он обратится к нему в обход PHP. Чтобы этого не произошло, скрипт считывает содержимое файла и «через себя» отправляет его на вывод. Такое может работать только на маленьких файлах, которые PHP может обработать, не затратив много серверных ресурсов и не использовав лимит времени. Если требуется отдавать посетителю большие файлы, то этот способ авторизации не будет функционировать.

Контроль доступа к файлам с использованием Apache mod_rewrite и эмуляции сессий в PHP.

Этот способ авторизации предназначен для контроля доступа к файлам для скачивания. Для контроля доступа пользователей к страницам сайта используйте PHP авторизацию при помощи сессий.

Чтобы контролировать доступ посетителей к файлам и при этом сохранить гибкость управления эти процессом, можно разделить обязанности между Apache и PHP. Apache будет отдавать файл, а PHP управлять правами доступа, но надо создать механизм связи между ними.

Авторизация пользователя проходит в два этапа: эмуляция сессии и определение mod_rewrite прав доступа к файлу. Сессии эмулируются для того, чтобы mod_rewrite мог получить статус авторизации посетителя.

Подготовка.

Файлы, для доступа к которым требуется авторизация, разместим в папке «download».
Для технологических файлов авторизации создадим папку «auth_files».

Первый этап — эмуляция сессии.

После аутентификации пользователя скрипт записывает код доступа в cookie посетителю и создаёт на стороне сервера файл, с именем, равным коду доступа.

Пример:

setcookie(«fileaccess», «QweRty123», time()+86400);
fopen(«/site_folder/auth_files /QweRty123», «w+»);
//header(«Location: downloadFile.avi»);

QweRty123 — произвольный код доступа

Второй этап — mod_rewrite и доступ к файлу

Модуль Apache — mod_rewrite может получить cookie пользователя с использованием %{HTTP_COOKIE}.

Для этого надо создать файл .htaccess который будет содержать следующие строки:

Options -Indexes
RewriteEngine on
RewriteBase /
# Получение кода доступа из cookie
RewriteCond %{HTTP_COOKIE} !fileaccess=([a-zA-Z0-9]+)
# Код доступа хранится в переменной %1.
# Строка ниже означает: Если требуемого файла не существует, то…
RewriteCond /site_folder/auth_files /%1 !-f
# То запретить доступ по URL, которые содержат строку «download/» (Ответ 403)
RewriteRule download/.* — [F]

Эта инструкция пропустит только тех посетителей, которые уже прошли авторизацию. Статус авторизации определяется по наличию на сервере файла, имя которого совпадает с кодом доступа в cookie посетителя.

В результате, скачивание файла происходит напрямую с сервера, а не через PHP скрипт. Так же пользователь имеет возможность докачивать файл.

Недостатком данного метода является необходимость очищать папку для технологических файлов «auth_files/» .

Если у пользователя есть доступ на сутки, то можно один раз в час запускать скрипт, который проверит учётные записи пользователей и удалит просроченные файлы авторизации.

Ниже приведена схема работы авторизации:

Схема работы авторизации и отдачи больших файлов

21 thoughts on “Контроль доступа к файлам с использованием Apache mod_rewrite и эмуляции сессий в PHP.”

  1. Интересная схема. Я бы даже сказал MR-надстройка.

    Как видится, полезна она для проектов, связанных с хранением и раздачей файлов.

    А вот если cookie отключены? =)

  2. Если отключены cookie, тогда пользователю не повезло… (Причём, ему не повезёт на многих сайтах.)

    Подругому отдавать большие файлы используя PHP+Apache не получится.

    PS: можно это сделать используя nginx, т.к. там есть нужный функционал для доступа пользователей к большим файлам.

    PPS: Можно попробовать обойти проблему с cookie использовав обычные сессии (идентификатор сессии — это код доступа), но т.к. идентификатор сессии может передаваться как с использованием GET, так и с использованием cookie, то такое разнообразие может увеличить количество возможных багов.

  3. Может быть кто-то поделится другими способами контроля доступа к очень большим файлам? Будет интересно!

  4. Хорошая работа!
    + в закладки.

    «Подготовка.
    Фалы, для доступа к которым требуется авторизация, положим в папку «download».»

    Очепятка в тексте: «фалы»(В качестве фалов используются металлические, синтетические или пеньковые тросы. Фал испытывает большую нагрузку при работе. Относится к бегучему такелажу)

    И «положим в папку» как то коряво звучит, «разместим в папке» думаю грамотнее будет.

    Удач!

  5. «И “положим в папку” как то коряво звучит, “разместим в папке” думаю грамотнее будет.»
    Спасибо, исправил.

    «Очепятка в тексте: “фалы”» — я заметил, что у меня Й на клавиатуре не всегда нажимается 🙂

  6. Надуманная проблема.

    Раздавать статику апачем — зло. Раз уж раздаете, значит по ресурсам еще и близко к потолку не подобрались. Значит можно в отдельной ветке апача и php-скрипт держать, который будет читать из файла порциями по 8К (объем tcp-окна) и отдавать юзеру. Особо не убудет 🙂

    Если уж так нравится mod_rewrite, то

    1) Файлов в папке auth_files прибывает очень быстро (это кол-во файлов в раздаче умножить на кол-во скачивающих юзеров). Когда их станет много, вы увидите затыки перед началом скачки — по 8, 20 секунд, а то и до таймаута недалеко..

    2) В доке по мод_рерайту есть отличный пример про Dynamic RewriteMap. Там правда пример на перле, но я думаю разберетесь — это именно ваш случай и именно в официальной доке.

    Но повторюсь, раздавать файлы апачем, да еще и с включенным мод_рерайтом — слегка странно. Если это для вас работает, то можно еще 10 разных способов понапридумывать, примерно таких же по эффективности. 🙂

  7. «Раздавать статику апачем — зло.»
    а раздавать модулем апача — это хорошо? Интересно. Или вы предлагаете написать сервер на пхп?

    «1) Файлов в папке auth_files прибывает очень быстро (это кол-во файлов в раздаче умножить на кол-во скачивающих юзеров).»
    Вы плохо читали. Файлов столько, сколько на данный момент активных пользователей (и ни какого умножения).

    «2) В доке по мод_рерайту есть отличный пример про Dynamic RewriteMap.»
    Я не хочу, чтобы mod_rewrite что-то парсил и искал строки в файле. Я не хочу записывать эти строки в этот файл и удалять устаревшие записи. Я думаю, что описанный мной метод минимизирует затраты.

  8. Не приспособлен апач для раздачи больших файлов. Лучше всех приспособлен nginx, но и он не без ложки дегтя — все процессы одновременно юзают все винты, разделить не получается и raid в данном случае не помощник. Но даже несмотря на это, все равно — на всех больших системах апач ставится как бекенд, а собственно веб-серверством занимается nginx, или lighttpd, или своя самописная приблуда, хоть бы даже и на перле. И уж что-что, а заставлять громадный апач форкаться, чтобы раздавать статические файлы, загружая при этом все модули, расширения, PHP, модреврайт (еще тот тормоз!), дергать локаль каждый раз..

    Вот по этому я и говорю — раздавайте скриптом и не мучайтесь. Юзайте cgi, форкайте его отдельным процессом — и пусть себе висит. На двухгиговом серваке таким образом легко можно держать под 1000 коннектов — просто, удобно и без заморочек. С нормальной авторизацией, с realtime статистикой и своим распределением нагрузки. Справится ли с таким количеством коннектов апач, если заставлять раздавать его с модреврайтом, как вы предлагаете — честно говоря не знаю. Но главное — зачем пробовать? 🙂

    Признайтесь, вы наверное php запускали как модуль?

  9. fogx, а можно купить себе 100 серверов, на каждый положить по одному файлу и будет совсем хорошо. Не впадайте в крайности.

    «Справится ли с таким количеством коннектов апач, если заставлять раздавать его с модреврайтом» — что значит раздавать с mod_rewrite? Модуль отработал один раз, проверил, можно ли пускать пользователя к файлу или нет, к процессу раздачи он ни какого отношения не имеет.
    Если бы я мог, то использовал бы nginx, но такой возможности у меня нет (думаю, как у большинства).

    «с realtime статистикой» — ну вот, ещё и статистику в общую кучу.

    А вот на счёт cgi наверно соглашусь. Звучит неплохо… Конечно хз, сейчас н могу всё понять, т.к. болею 🙁

  10. Дело в том, что простая строка в хтаксесе RewriteEngine ON уже заставляет апач делать кучу проверок при каждом GET (а на 700 меговый файл у вас будет не один GET, а много). Так что к процессу раздачи отношение самое непосредственное. Почему именно модрерайт настолько сказывается на производительности, я и сам не понимаю. Но факт..

    А realtime статистику в кучу я приплел просто потому, что раздавать скриптом — крайне УДОБНО. Можно самому дропать медленные конекшены (или бесплатных юзеров 🙂 )
    можно динамически выделять память под буфер в зависимости от скорости, ограничивать эту скорость, распределять нагрузку на винты и т.д. Короче плюсов масса.

    И кстати собственный веб-сервер — это далеко не так страшно, как многим почему-то кажется. Вам же не надо повторять функционал апача, наоборот, вы его как раз и делаете, чтобы он был маленький и имел только самое необходимое. Любой двадцатикилобайтный троян имеет на борту встроенный веб-сервер, который умеет раздавать файлы, делать директори листинг, авторизацию и еще всякое по мелочи. Это действительно просто. Добавить сюда обработки If-Modified-Since, Range, убрать директори листинг, ибо он нафиг не нужен, ну и собственно все.. Дальше смотреть по задачам. Но главное — у вас полная свобода действий! А чуть какой нестандартный запрос пришел — так вот он апач под боком, пусть разбирается 🙂

  11. Свой сервер — это не страшно, это очень удобно, но его нет :).

    Индивидуальные настройки и окружение это просто супер, но, по факту, у меня есть apache и php на почти стандартном хостинге.

    Для меня, использованеи mod_rewrite наиболее приемлимый способ.

    Что будет более ресурсоёмким, apache или cgi:php-скрпит, я не знаю… И я не особо представляю, как можно это проверить.

  12. Сегодня прпотестировал на Winnows.

    1) Если запускать PHP как CGI, то он занимает ~4,5Мб памяти.
    2) Каждое обращение к CGI:PHP будет провоцировать создание нового процесса.

    fogx писал, что 1000 коннектов уронят Apache, и что надо использовать CGI скрипт.

    Но 1000 коннектов, это 1000 одновременно запущеных процессов PHP, что в итоге вытекает в ~4500Мб память.

    Но я о таких нагрузках не говорил, т.к. если есть такая посещаемость, то проблему надо решать совершенно другими способами!

    Но даже при 100 одновременных скачивания файлов будет занято ~450Мб памяти, что мне кажетсо слишком. Уж лучше пусть висит один apache.

  13. На смом деле такую задачу, можно реализовать еще проще 🙂
    Вы создаете символические ссылки на большие файлы, запоминая их имена в базе и выдаете авторизированным пользователям, ну и конечно заменяете их, скажем, раз в сутки. Это конечно даст возможность качать по символической ссылке, но не более, чем период «переименования» 🙂

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

  15. Дядя Ваня: Авторизованный пользователь может опубликовать ссылку (например в своём твиттере) и файл сможет качать кто угодно.

  16. А как сделать то-же, но чтоб отдать файл по прямой ссылке только при наличии в PHP пользовательской переменной и с определенным значением?

Добавить комментарий

Ваш e-mail не будет опубликован.