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

Владимир | | CodeIgniter, PHP, Web разработка.

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

И, прежде всего, разберем взаимодействие между посетителем, сервером и 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 и добавляем в него следующие настройки.

<IfModule mod_rewrite.c>
	# загружаем файл со списком временных ссылок
	# ключ в массиве - ip_ссылка, значение - название файла и время жизни ссылки
	RewriteMap temp-links "txt:/your_path/templinks.txt"
</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 с такими директивами.

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteBase /path_to_site/

	# если не найдена запись для данного посетителя и файла (IP-ИмяФайла)
	RewriteCond ${temp-links:%{REMOTE_ADDR}-$2|DENIED} =DENIED [OR]
	
	# или время работы этой записи закончилось
	# (время в формате ГодМесяцДеньЧасыМинутыСекунды (например, 20090111213503))
	RewriteCond ${temp-links:%{REMOTE_ADDR}-$2|0} <${TIME}
	
	# и если это не запрос к статическому файлу сайта (картинки, css и т.п., естественно, archive сюда не входит)
	RewriteCond $1/$2 !^(index\.php|images|robots\.txt|css)
	
	# перенаправляем запрос 
	RewriteRule ^(.*)/([a-zA-Z0-9-_.]+)$ index.php/$1/$2 [L]
</IfModule>

<IfModule !mod_rewrite.c>
	ErrorDocument 404 /index.php
</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 уже не нужен.

class MFileLink extends Model {

	function MFileLink() {
		parent::Model();
	}
	
	/**
	 * Создает временную ссылку и добавляет ее в текстовый файл
	 *
	 * @param $fileName - имя файла
	 * @return ссылку, если она создана, false - если возникла ошибка
	 */
	function createLink($fileName) {
		$userIP = $_SERVER['REMOTE_ADDR'];
		
		//проверяем существование файла
		if (!get_file_info($this->config->item('filesdir').'/'.$fileName)) {
			return false;
		}

		if (write_file('templinks.txt', $userIP.'-'.$fileName.' '
			.date('YmdHis', time() + $this->config->item('linktime')).PHP_EOL, 'a')) {
			return 'archive/'.$fileName;
		}
		else {
			return false;
		}
	}
}

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

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

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

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

Скачать:

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

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

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

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

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

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

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