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

15 января, 2009

Прежде всего, обратите внимание на то, что все настройки мы храним в отдельном конфигурационном файле application/config/downloader.php.

<?php
$config['filesdir'] = 'archive'; //имя папки с файлами (которые мы разрешаем скачивать)
$config['linktime'] = '120'; //время жизни ссылки на файл (в сек)
?>

Загрузка этого файла выполняется к конструкторе контроллера (строка 14).

Метод index формирует страницу со списком файлов, которые находятся в папке archive. Чтение содержимого папки выполняется с помощью функции get_filenames (входит в File Helper фреймворка), а создание страницы происходит в представлении (application/views/filelist.php).

<?php
//показываем сообщение об ошибке, если оно есть
if ($this->session->flashdata('error')) {
	echo '<h3>'.$this->session->flashdata('error').'</h3>';
}
?>
<h2>Список файлов</h2>
<ol>
<?php
if ($files) {
	foreach ($files as $file) {
		echo '<li>'.anchor('main/confirm/'.$file, $file).'</li>';
	}
}
else {
	echo '<p>Файлы не найдены</p>';
}
?>
</ol>

Как видите, ссылка на каждый из файлов, ведет на страницу main/confirm/file (строка 12), т.е. вызывает метод confirm контроллера и передает в качестве параметра имя файла.

Теперь разберем метод confirm. Он создает страницу с простой математической captcha, которую должен ввести посетитель для того, чтобы скачать файл.

Если captcha введена правильно, то будет создана временная ссылка (об этом чуть ниже) и отправлен редирект на страницу загрузки файла (main/download/link) (строка 50). В противном случае, посетитель останется на этой же странице.

Переходим к методу download. Здесь мы выполняем проверку временной ссылки и отправляем файл посетителю (строки 102 и 105).

Непосредсвенно отправка файла выполняется в методе _sendFile с помощью функции force_download (тоже входит в File Helper).

Если вы внимательно просматривали код, то конечно заметили использование модели mfilelink.

<?php
/**
 * Эта модель предназначена для работы с временными ссылками на файлы
 *
 * @link http://www.simplecoding.org
 * @author Стаценко Владимир <vova_33@gala.net>
 */
class MFileLink extends Model {

	function MFileLink() {
		parent::Model();
	}

	/**
	 * Создает временную ссылку и добавляет ее в базу
	 *
	 * @param $fileName - имя файла
	 * @return ссылку, если она создана, false - если возникла ошибка
	 */
	function createLink($fileName) {
		$userIP = $_SERVER['REMOTE_ADDR'];
		$fileLink = md5($userIP.time());

		//проверяем существование файла
		if (!get_file_info($this->config->item('filesdir').'/'.$fileName)) {
			return false;
		}

		$qCreateLink = 'INSERT INTO file_downloads (ip, fileLink, expired, fileName)'
			.' VALUES (?, ?, NOW() + INTERVAL '.$this->config->item('linktime').' SECOND, ?)';
		if ($this->db->query($qCreateLink,
			array(
				$userIP,
				$fileLink,
				$fileName
		))) {
			return $fileLink;
		}
		else {
			return false;
		}
	}

	/**
	 * Проверяет временную ссылку
	 *
	 * @param $link - временная ссылка на файл
	 * @return имя файла - если ссылка рабочая, false - если нет.
	 */
	function checkFileLink($link) {
		$userIP = $_SERVER['REMOTE_ADDR'];

		$qCheckLink = 'SELECT fileName FROM file_downloads WHERE ip=? AND fileLink=? AND (expired >= NOW())';
		$res = $this->db->query($qCheckLink, array($userIP, $link));

		if ($res->num_rows() == 1) {
			$r = $res->result_array();
			return $r[0]['fileName'];
		}
		else {
			return false;
		}
	}
}
?>

Как видите, она содержит всего два метода (не считая конструктора).

1) createLink – создает временную ссылку на выбранный файл и сохраняет её в базе данных.

2) checkFileLink – проверяет, можно ли данному посетителю скачивать файл. Для этого, метод ищет подходящую запись в базе данных. Должны совпадать IP, имя ссылки и время завершения её действия должно быть меньше текущего.

Теперь осталось только закрыть доступ к папке archive.

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

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

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond $1 !^(index\.php|images|robots\.txt|css)
    RewriteRule ^(.*)$ index.php/$1 [L]

    RewriteRule ^(archive.*) index.php/$1 [L]
</IfModule>

<IfModule !mod_rewrite.c>
    ErrorDocument 404 /index.php
</IfModule>

Здесь для нас важно только одно правило (строка 10). С его помощью ко всем запросы, которые начинаются со слова archive добавляется index.php. Таким образом, запрос вроде mysite.com/archive/file.name превращается в mysite.com/index.php/archive/file.name.

В результате запрос будет обработан движком CodeIgniter и посетитель увидит стандартную страницу с 404-ой ошибкой.

Продолжение на следующей странице >>>

Страницы: 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 разработка Комментарии (10) »

]]>

Вы можете оставить комментарий. Трекбеки закрыты.

  • 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{
    вывод страницы где пользователю надо что-то ввести
    }

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

    Может метод и является странным но имеет право на жизнь

    • http://www.simplecoding.org/ Владимир

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

      Вынос файлов за пределы DOCUMENT_ROOT тоже вариант. Можно не думать о том как запретить доступ к файлам.
      Но остается проблема – высокое потребление ресурсов при отдаче через PHP скрипт.

    • Gad666999

      для кого вообще в самом начале писали о потреблении ресурсов и их ограничение через php. readfile читает файл и в буфер кидает, весело будет 2гб читать.
      А такой способ в статье тоже не особо, представьте у вас куча поситителей и куча файлов, мод реврайт будет свой массив всё время юзать в том время когда текстовой файл разростёться до огромной базы ip адресов

      • http://www.simplecoding.org Владимир

        Высоконагруженная система – это в любом случае проблема. Но если нужно контролировать раздачу (запретить кому-то доступ), то вести учет пользователей (их ip или каких-то других данных) все-равно придется.
        А устаревшие ip можно удалять отдельным скриптом (его можно запускать по расписанию).

    • Alex

      Ивините, но после того как я скачал архив с первым вариантом и выполнил инструкции написанные в файле install. Я что-то не могу понять, что написать в файле index.php, что бы как то проверить действие вашего скрипта…

      • http://www.simplecoding.org Владимир

        Вы ничего не должны писать в index.php. После установки фреймворка CI и скрипта с примером, просто заходите на ваш локальный сайт через браузер.

  • 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{
    вывод страницы где пользователю надо что-то ввести
    }

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

    Может метод и является странным но имеет право на жизнь

    • http://www.simplecoding.org/ Владимир

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

      Вынос файлов за пределы DOCUMENT_ROOT тоже вариант. Можно не думать о том как запретить доступ к файлам.
      Но остается проблема – высокое потребление ресурсов при отдаче через PHP скрипт.

  • http://rise.zexeor.com/ Galla

    Спасибо помогло!

  • http://rise.zexeor.com/ Galla

    Спасибо помогло!

]]>
Tweet