О том, как сделать доступ к большим файлам только после авторизации посетителя на сайте.
Введение.
Для контролируемого доступа посетителей на сайт обычно используют авторизацию при помощи 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/» .
Если у пользователя есть доступ на сутки, то можно один раз в час запускать скрипт, который проверит учётные записи пользователей и удалит просроченные файлы авторизации.
Ниже приведена схема работы авторизации:

Интересная схема. Я бы даже сказал MR-надстройка.
Как видится, полезна она для проектов, связанных с хранением и раздачей файлов.
А вот если cookie отключены? =)
Если отключены cookie, тогда пользователю не повезло… (Причём, ему не повезёт на многих сайтах.)
Подругому отдавать большие файлы используя PHP+Apache не получится.
PS: можно это сделать используя nginx, т.к. там есть нужный функционал для доступа пользователей к большим файлам.
PPS: Можно попробовать обойти проблему с cookie использовав обычные сессии (идентификатор сессии – это код доступа), но т.к. идентификатор сессии может передаваться как с использованием GET, так и с использованием cookie, то такое разнообразие может увеличить количество возможных багов.
Без кукисов вообще делать нечего в современном Интернете, так что нормально
Может быть кто-то поделится другими способами контроля доступа к очень большим файлам? Будет интересно!
Хорошая работа!
+ в закладки.
«Подготовка.
Фалы, для доступа к которым требуется авторизация, положим в папку «download».»
Очепятка в тексте: «фалы»(В качестве фалов используются металлические, синтетические или пеньковые тросы. Фал испытывает большую нагрузку при работе. Относится к бегучему такелажу)
И «положим в папку» как то коряво звучит, «разместим в папке» думаю грамотнее будет.
Удач!
«И “положим в папку” как то коряво звучит, “разместим в папке” думаю грамотнее будет.»
Спасибо, исправил.
«Очепятка в тексте: “фалы”» – я заметил, что у меня Й на клавиатуре не всегда нажимается
Спасибо большое за инфу! Очень помогло!
Надуманная проблема.
Раздавать статику апачем – зло. Раз уж раздаете, значит по ресурсам еще и близко к потолку не подобрались. Значит можно в отдельной ветке апача и php-скрипт держать, который будет читать из файла порциями по 8К (объем tcp-окна) и отдавать юзеру. Особо не убудет
Если уж так нравится mod_rewrite, то
1) Файлов в папке auth_files прибывает очень быстро (это кол-во файлов в раздаче умножить на кол-во скачивающих юзеров). Когда их станет много, вы увидите затыки перед началом скачки – по 8, 20 секунд, а то и до таймаута недалеко..
2) В доке по мод_рерайту есть отличный пример про Dynamic RewriteMap. Там правда пример на перле, но я думаю разберетесь – это именно ваш случай и именно в официальной доке.
Но повторюсь, раздавать файлы апачем, да еще и с включенным мод_рерайтом – слегка странно. Если это для вас работает, то можно еще 10 разных способов понапридумывать, примерно таких же по эффективности.
«Раздавать статику апачем – зло.»
а раздавать модулем апача – это хорошо? Интересно. Или вы предлагаете написать сервер на пхп?
«1) Файлов в папке auth_files прибывает очень быстро (это кол-во файлов в раздаче умножить на кол-во скачивающих юзеров).»
Вы плохо читали. Файлов столько, сколько на данный момент активных пользователей (и ни какого умножения).
«2) В доке по мод_рерайту есть отличный пример про Dynamic RewriteMap.»
Я не хочу, чтобы mod_rewrite что-то парсил и искал строки в файле. Я не хочу записывать эти строки в этот файл и удалять устаревшие записи. Я думаю, что описанный мной метод минимизирует затраты.
Не приспособлен апач для раздачи больших файлов. Лучше всех приспособлен nginx, но и он не без ложки дегтя – все процессы одновременно юзают все винты, разделить не получается и raid в данном случае не помощник. Но даже несмотря на это, все равно – на всех больших системах апач ставится как бекенд, а собственно веб-серверством занимается nginx, или lighttpd, или своя самописная приблуда, хоть бы даже и на перле. И уж что-что, а заставлять громадный апач форкаться, чтобы раздавать статические файлы, загружая при этом все модули, расширения, PHP, модреврайт (еще тот тормоз!), дергать локаль каждый раз..
Вот по этому я и говорю – раздавайте скриптом и не мучайтесь. Юзайте cgi, форкайте его отдельным процессом – и пусть себе висит. На двухгиговом серваке таким образом легко можно держать под 1000 коннектов – просто, удобно и без заморочек. С нормальной авторизацией, с realtime статистикой и своим распределением нагрузки. Справится ли с таким количеством коннектов апач, если заставлять раздавать его с модреврайтом, как вы предлагаете – честно говоря не знаю. Но главное – зачем пробовать?
Признайтесь, вы наверное php запускали как модуль?
Как ни странно, но я с fogx соглашусь. Дельные вещи об Apache говорит. Для 1000 коннектов он действительно тяжеловесен.
fogx, а можно купить себе 100 серверов, на каждый положить по одному файлу и будет совсем хорошо. Не впадайте в крайности.
«Справится ли с таким количеством коннектов апач, если заставлять раздавать его с модреврайтом» – что значит раздавать с mod_rewrite? Модуль отработал один раз, проверил, можно ли пускать пользователя к файлу или нет, к процессу раздачи он ни какого отношения не имеет.
Если бы я мог, то использовал бы nginx, но такой возможности у меня нет (думаю, как у большинства).
«с realtime статистикой» – ну вот, ещё и статистику в общую кучу.
А вот на счёт cgi наверно соглашусь. Звучит неплохо… Конечно хз, сейчас н могу всё понять, т.к. болею
Дело в том, что простая строка в хтаксесе RewriteEngine ON уже заставляет апач делать кучу проверок при каждом GET (а на 700 меговый файл у вас будет не один GET, а много). Так что к процессу раздачи отношение самое непосредственное. Почему именно модрерайт настолько сказывается на производительности, я и сам не понимаю. Но факт..
А realtime статистику в кучу я приплел просто потому, что раздавать скриптом – крайне УДОБНО. Можно самому дропать медленные конекшены (или бесплатных юзеров
)
можно динамически выделять память под буфер в зависимости от скорости, ограничивать эту скорость, распределять нагрузку на винты и т.д. Короче плюсов масса.
И кстати собственный веб-сервер – это далеко не так страшно, как многим почему-то кажется. Вам же не надо повторять функционал апача, наоборот, вы его как раз и делаете, чтобы он был маленький и имел только самое необходимое. Любой двадцатикилобайтный троян имеет на борту встроенный веб-сервер, который умеет раздавать файлы, делать директори листинг, авторизацию и еще всякое по мелочи. Это действительно просто. Добавить сюда обработки If-Modified-Since, Range, убрать директори листинг, ибо он нафиг не нужен, ну и собственно все.. Дальше смотреть по задачам. Но главное – у вас полная свобода действий! А чуть какой нестандартный запрос пришел – так вот он апач под боком, пусть разбирается
Свой сервер – это не страшно, это очень удобно, но его нет
.
Индивидуальные настройки и окружение это просто супер, но, по факту, у меня есть apache и php на почти стандартном хостинге.
Для меня, использованеи mod_rewrite наиболее приемлимый способ.
Что будет более ресурсоёмким, apache или cgi:php-скрпит, я не знаю… И я не особо представляю, как можно это проверить.
Сегодня прпотестировал на Winnows.
1) Если запускать PHP как CGI, то он занимает ~4,5Мб памяти.
2) Каждое обращение к CGI:PHP будет провоцировать создание нового процесса.
fogx писал, что 1000 коннектов уронят Apache, и что надо использовать CGI скрипт.
Но 1000 коннектов, это 1000 одновременно запущеных процессов PHP, что в итоге вытекает в ~4500Мб память.
Но я о таких нагрузках не говорил, т.к. если есть такая посещаемость, то проблему надо решать совершенно другими способами!
Но даже при 100 одновременных скачивания файлов будет занято ~450Мб памяти, что мне кажетсо слишком. Уж лучше пусть висит один apache.
На смом деле такую задачу, можно реализовать еще проще
Вы создаете символические ссылки на большие файлы, запоминая их имена в базе и выдаете авторизированным пользователям, ну и конечно заменяете их, скажем, раз в сутки. Это конечно даст возможность качать по символической ссылке, но не более, чем период «переименования»
Дядя Ваня: Отдачу файлов на основе тухлых ссылок можно использовать как часть защиты от хотлинкинга, но не как авторизацию, т.к. она не обеспечивает возможность скачивания файла исключительно авторизованными пользователями.
Andrey Shatrov: А почему нельзя выдавать ссылки, хранящиеся в базе только авторизированным пользователям?
Дядя Ваня: Авторизованный пользователь может опубликовать ссылку (например в своём твиттере) и файл сможет качать кто угодно.