Ограничиваем доступ к файлам на сервере

15 января, 2009

Теперь рассмотрим второй вариант решения задачи.

И, прежде всего, разберем взаимодействие между посетителем, сервером и PHP скриптом в этом случае.

apache_file_download_thumb

Сравните его с предыдущим способом решения. С первого взгляда видно, что этот при этом варианте обращений к PHP будет гораздо меньше, т.к. всю работу по проверке прав доступа к файлу мы переложили на Apache.

Чтобы скачать файл, как и в предыдущем случае, посетителю нужно зайти на сайт, выбирать файл и ввести captcha. После этого PHP скрипт создает запись в специальном файле (templinks.txt), в которой указываются:

1) IP адрес посетителя;
2) имя файла, который он хочет скачать;
3) время окончания действия этой записи (т.е. время до которого разрешается скачивание).

Примечание. В отличие от предыдущего варианта сейчас мы используем текстовый файл, а не БД, для хранения данных о загрузках посетителей. Дело в том, что mod_rewrite может работать с текстовым файлом без использования дополнительных скриптов, а с БД – нет.

Если кто-то обратиться к файлу напрямую (без ввода captcha), а сервер не найдет в файле templinks.txt записи для этого посетителя, то, как и в предыдущем случае, к запросу будет добавлен index.php, и вместо файла посетитель получит страницу с 404-ой ошибкой.

Теперь посмотрим, как рассказать серверу, как проверять запросы к файлам.

Для этого открываем файл httpd.conf и добавляем в него следующие настройки.

  1. <IfModule mod_rewrite.c>
  2.     # загружаем файл со списком временных ссылок
  3.     # ключ в массиве – ip_ссылка, значение – название файла и время жизни ссылки
  4.     RewriteMap temp-links "txt:/your_path/templinks.txt"
  5. </IfModule>

Здесь у нас только одна директива – RewriteMap. Именно она читает данные из файла templinks.txt и помещает их в ассоциативный массив temp-links.

Примечание. К сожалению, директиву RewriteMap нельзя размещать в файлах .htaccess, поэтому доступ к конфигу сервера обязателен.

Я сразу хочу отметить, что mod_rewrite очень чувствителен к ошибкам, поэтому будьте особенно внимательны когда записываете директивы.

Переходим к формату записей в файле templinks.txt.

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

Первое слово используется в качестве ключа ассоциативного массива (temp-links), второе – в качестве значения.

Поэтому я решил использовать следующий формат:

IPадрес-ИмяФайла Дата

Например, запись:
127.0.0.1-file4.txt 20090111234431
означает, что посетитель с IP адресом 127.0.0.1 может скачивать файл file4.txt до 23:44:31 11 января 2009 года.

Теперь в корне сайта создаем .htaccess с такими директивами.

  1. <IfModule mod_rewrite.c>
  2.     RewriteEngine On
  3.     RewriteBase /path_to_site/
  4.  
  5.     # если не найдена запись для данного посетителя и файла (IP-ИмяФайла)
  6.     RewriteCond ${temp-links:%{REMOTE_ADDR}-$2|DENIED} =DENIED [OR]
  7.    
  8.     # или время работы этой записи закончилось
  9.     # (время в формате ГодМесяцДеньЧасыМинутыСекунды (например, 20090111213503))
  10.     RewriteCond ${temp-links:%{REMOTE_ADDR}-$2|0} <${TIME}
  11.    
  12.     # и если это не запрос к статическому файлу сайта (картинки, css и т.п., естественно, archive сюда не входит)
  13.     RewriteCond $1/$2 !^(index\.php|images|robots\.txt|css)
  14.    
  15.     # перенаправляем запрос
  16.     RewriteRule ^(.*)/([a-zA-Z0-9-_.]+)$ index.php/$1/$2 [L]
  17. </IfModule>
  18.  
  19. <IfModule !mod_rewrite.c>
  20.     ErrorDocument 404 /index.php
  21. </IfModule>

Это, наверное, самая сложная часть ;)

С помощью директив RewriteCond (строки 6 и 10) мы определяем есть ли подходящая запись в массиве temp-links для данного запроса или нет. Разберем их по частям.

Выражение
${temp-links:%{REMOTE_ADDR}-$2|DENIED}
обрабатывается следующим образом.

1) Получаем IP адрес (%{REMOTE_ADDR}).

2) Добавляем к нему дефис и вторую часть адреса $2. Это интересный момент. Директивы RewriteCond не существуют сами по себе, они всегда связаны с RewriteRule (строка 16), в которой мы разбираем адрес с помощью регулярного выражения. Т.е. $2 соответствует второй подмаске этого выражения (тому что находится во второй паре круглых скобок).

3) С помощью полученного ключа (IPадрес-ИмяФайла) ищем подходящее значение в массиве temp-links.

4) Если значение с таким ключем отсутствует в массиве, то будет использовано DENIED.

Вторая директива RewriteCond выполняет проверку времени. Ключ массива составляется точно так же как и в предыдущем случае, но в качестве значения по-умолчанию используется не DENIED, а ноль. Значение найденного элемента в массиве сравнивается с текущим временем.

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

В противном случае в начало запроса добавляем index.php и таким образом управление передается движку CodeIgniter.

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

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

Нам нужны выполнить только три операции:

1) создать страницу с общим списком файлов;

2) добавить форму с captcha;

3) если captcha заполнена правильно, то добавить запись в файл templinks.txt.

Я не вижу смысла приводить здесь код контроллера. Он остался практически без изменений, только убраны методы download и _sendFile. Да и в любом случае, вы можете скачать архив с примером.

А вот код модели изменился более существенно. Теперь мы работаем с текстовым файлом, а не базой данных, да и метод checkFileLink уже не нужен.

  1. class MFileLink extends Model {
  2.  
  3.     function MFileLink() {
  4.         parent::Model();
  5.     }
  6.    
  7.     /**
  8.      * Создает временную ссылку и добавляет ее в текстовый файл
  9.      *
  10.      * @param $fileName – имя файла
  11.      * @return ссылку, если она создана, false – если возникла ошибка
  12.      */
  13.     function createLink($fileName) {
  14.         $userIP = $_SERVER['REMOTE_ADDR'];
  15.        
  16.         //проверяем существование файла
  17.         if (!get_file_info($this->config->item('filesdir').'/'.$fileName)) {
  18.             return false;
  19.         }
  20.  
  21.         if (write_file('templinks.txt', $userIP.'-'.$fileName.' '
  22.             .date('YmdHis', time() + $this->config->item('linktime')).PHP_EOL, 'a')) {
  23.             return 'archive/'.$fileName;
  24.         }
  25.         else {
  26.             return false;
  27.         }
  28.     }
  29. }

Удаление устаревших ссылок и записей.

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

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

Приводить его код я здесь не буду, статья и так получилась довольно объемной. К тому же, я уверен, что вы без особого труда и сами сможете его написать ;)

Скачать:

1) Архив с первым вариантом (отправка файлов PHP скриптом).

2) Архив со вторым вариантом (отправка с помощью Apache).

Примечание. Я хочу подчеркнуть, что эту статью нельзя рассматривать как руководство для создания сервисов файлового хостинга (file share). Т.к. остались не рассмотренными вопросы вроде распределения нагрузки между серверами, да и сам Apache потребляет довольно много ресурсов (связка nginx + Apache скорее всего будет более удачным выбором для такого сервиса).

Если вы заметили ошибку, хотите рассказать о других вариантах решения задачи или просто высказаться, не стесняйтесь, комментарии открыты ;)

Интересно почитать

Интернет-магазин предлагает множество уникальных сортов чая продажа чая.
Не с чем идти на день рожденья? Плюшевые игрушки идеально подойдут любому ребенку в качестве подарка.
Что выбрать: платный хостинг или бесплатный? Все просто, в первом случае – хостинг будет действительно ваш.

Страницы: 1 2 3

Понравилась статья? Подписывайтесь на продолжение rss link !

Или на мой твиттер twitter link

]]>

Добавьте эту страницу в google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

]]>

Опубликовано в CodeIgniter, PHP, Web разработка View Comments

]]>
  • Спасибо помогло!
  • qnikst
    ну не знаю, наверное я не модный но существует такой вариант:
    1). все файлы лежат в папке филес вне корневого раздела, доступ к которому открыт для пхп через open_basedir. В базе данных хранятся их атрибуты, чтобы не тратить время на мученье hdd.
    2). создаётся файл php файл вида data в корне и прописывается в htaccess то что это скрипт.
    3). доступ к файлам осуществяется через /data/path/filename:
    в файле data есть следующие строки:

    $url_array = explode("/", $_SERVER["REQUEST_URI"]);//получили путь
    if (/* условие того, что юзер подтвердил файл капчу и т.д. */){
    $dev = mysql_query("select type,name,leght ...");
    //получение данных
    header("Content-Type: application/x-force-download;name='name'");
    header("Content-Length: $length");
    // и т.д.
    @readfile(dirname($_SERVER['DOCUMENT_ROOT']).'/files/'.$filename);
    }else{
    вывод страницы где пользователю надо что-то ввести
    }


    В общем чтобы не было лишний слов, скажу, что все проверки на валидность и прочие естественные вещи я не писал, только общую суть

    Может метод и является странным но имеет право на жизнь
  • Согласен, список файлов лучше хранить в базе, а не читать каждый раз с винчестера. Хотя если страница кэшируется, то разница небольшая.

    Вынос файлов за пределы DOCUMENT_ROOT тоже вариант. Можно не думать о том как запретить доступ к файлам.
    Но остается проблема - высокое потребление ресурсов при отдаче через PHP скрипт.
blog comments powered by Disqus ]]>