dervan |
|
---|---|
Ratibor, разбирался я как-то с этим вопросом.
Про реализацию на основе ndl-2003-05-19_10.zip. Подход верный, но контроль допустимости ссылки на закрытый файл сделан через REFERER - это неправильно, из-за этого неизбежны дыры. Попытки ограничить доступ к файлам проверкой REFERER ненадёжны и годятся разве что для простого anti-hotlinking'а, который можно сделать в .htaccess средствами mod_rewrite. Во-первых, REFERER легко подделать. Во-вторых, HTTP-запрос с пустым REFERER нельзя отвергать, но если его не отвергать - опять же получится дыра. Пример подделки REFERER. В ReGet Deluxe Personal добавляем закачку: на закладке "Общие" в поле "URL:" вбиваем косвенную ссылку "http://www.my_site.ru/my_files/examples/example02/get.php?file=test", на закладке "HTTP" в поле "Ссылка" вбиваем её же. После этого файл будет успешно скачан, несмотря на отсутствие прямой ссылки на него. Пример с пустым REFERER. Браузер посетителя - Opera 9 со снятым чекбоксом: Tools -> Preferences... -> Advanced -> Network -> Send referrer information (Инструменты -> Настройки... -> Дополнительно -> Сеть -> Отправлять данные о ссылающейся странице). Браузер не будет отправлять REFERER, и посетитель не сможет скачивать файлы, хотя и будет идти по ссылкам со страницы http://www.my_site.ru/my_files/examples/examples.html. Реализация надёжного упрятывания файлов. 1. К закрытым файлам не должно быть доступа по HTTP, т.е. прямых ссылок на них вообще не существует. 2. Следствие из п.1: отправку файла по запросу HTTP должен делать скрипт, работающий аналогично HTTP-серверу - скрипт, считывающий файл с диска и отправляющий его в сеть в соответствии с протоколом HTTP. 3. Данные для работы скрипта, отправляющего файл, должны формироваться при заходе посетителя на страницу со ссылкой на скачивание, и эти данные должны быть привязаны к этому заходу. Что сделано в приведённой тобой реализации. 1. Этот пункт реализован, но усложнённо - применение mod_rewrite здесь избыточно. Можно реализовать проще: завести каталог для закрытых файлов, например indirect, и положить в него блокирующий .htaccess: <Files ~ "*"> order allow,deny deny from all </Files> 2. Этот пункт тоже реализован. Отправку файла по протоколу HTTP делает класс NDL (файл ndl.class.php). Посмотрел, как формируются HTTP-заголовки, видно что реализация упрощённая - например, не формируется рекомендованный заголовок ETag. Может, и будет нормально работать, но IMHO лучше прикрутить PEAR HTTP_Download. 3. Привязки к заходу на страницу нет вообще. Сделана проверка REFERER классом ANDL (файл andl.class.php), но такая проверка - дырявая. Вместо этого можно сделать реализацию через сессию. 1) Реализация при отключенном кешировании страниц. Путь к закрытому файлу содержится на нераспарсенной странице в специальном bbcode, например с префиксом @: [url=@indirect/test.zip]test.zip[/url] При заходе на страницу парсер bbcode получает из пути ключ. С этим ключом парсер создаёт переменные в сессии:
$sesskey = md5('indirect/test.zip');
$_SESSION['indirect'][$sesskey]['fspec'] = 'indirect/test.zip';
$_SESSION['indirect'][$sesskey]['uid'] = $usr['id'];
$body .= "<a href=\"indirect.php?$sesskey\">test.zip</a>";
Действия скрипта indirect.php:
< ? php
if (!isset($_REQUEST[session_name()]))
{
exit; // невозможно возобновить сессию
}
session_start();
if (empty($_SERVER['QUERY_STRING'])
|| !isset($_SESSION['indirect'][$_SERVER['QUERY_STRING']]) // данные не сформированы, не было захода на страницу со ссылкой
|| !file_exists($fspec = $_SESSION['indirect'][$_SERVER['QUERY_STRING']]['fspec'])
|| $_SESSION['indirect'][$_SERVER['QUERY_STRING']]['uid'] < 1 // закачка разрешена только зарегестрированным посетителям
)
{
exit;
}
// отправка файла $fspec - PEAR HTTP_Download, либо класс NDL
...
2) Реализация при включенном кешировании страниц. Надо ввести специальную страницу download.php, которая никогда не кешируется. Ссылки с кешированных страниц будут идти на неё, и при заходе на эту страницу download.php в сессии будут создаваться переменные для indirect.php, а на самой странице download.php будет помещена ссылка на скрипт indirect.php, отправляющий файл:
$body .= "<a href=\"indirect.php?$sesskey\">test.zip</a>";
|