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

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

В этой статье я хочу рассказать о нескольких вариантах ограничения доступа к файлам на сервере.

Т.е. мы сделаем так, чтобы посетитель перед загрузкой в обязательном порядке выполнил какие-то действия (например, вводил captcha).

Существует два основных варианта решения задачи.

1) Скачиванием файлов будет управляет PHP скрипт.

2) Скачивание происходит через web сервер, но только в том случае, если у посетителя есть соответствующее разрешение.

Прежде всего, разберем достоинства и недостатки обоих методов.

Первый вариант. Вообще-то единственное его достоинство – это возможность реализации на shared-хостинге.

Зато недостатки очень серьезные. Файл отправляется пользователю через PHP скриты, т.е. работают все ограничения, действующие на них. Например, если для скриптов установлено максимальное время работы 15 сек, то значит, файл должен быть загружен за это время. Тоже самое и с максимально допустимым объемом памяти.

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

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

Второй вариант выглядит значительно привлекательнее. Ситуация с достоинствами и недостатками полностью противоположная. Отдавать файлы будет Apache без использования PHP. Но чтобы реализовать проверку посетителей, нужно иметь доступ к конфигу сервера (httpd.conf).

Рассмотрим общий принцип загрузки файла


file_download_thumb

Как видите, после запроса файла посетителем, сервер проверяет, можно ли отдавать ему данный файл. Реализовать эту проверку можно двумя способами:

1) с помощью PHP скрипта (конечно, это может быть скрипт на любом другом языке);

2) с помощью встроенных средств web сервера Apache (речь, конечно, идет о mod_rewrite).

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

Теперь посмотрим, что должно происходить, если доступ для данного посетителя к файлу закрыт.

В этом случае необходимо сформировать страницу с описанием ошибки и отправить её посетителю. Что именно будет содержать эта страница – зависит от требований к конкретному сайту и большой роли для этого примера не играет.

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

Для этого примера я выбрал два параметра: IP адрес и время доступа к файлу.

Принцип работы следующий.

1) Посетитель заходит на сайт и выбирает нужный файл.

2) Вводит captcha (captcha я взял исключительно ради примера, её можно заменить какой-нибудь другой формой).

3) Скрипт создает запись с IP адресом посетителя, названием файла и временем, в течении которого можно скачивать файл. Запись может находиться либо в БД, либо в обычном текстовом файле, на данном этапе это роли не играет.

4) Либо web сервер, либо PHP скрипт отправляют посетителю запрошенный файл.

5) Если посетитель повторно запрашивает файл, то сервер ищет его IP, проверяет время доступа и, в зависимости от результатов, отправляет файл или страницу с ошибкой.

Теперь рассмотрим варианты реализации.

Начнем с более простого варианта – отправки через PHP скрипт.

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

На рисунке показан алгоритм работы сайта.

php_file_download_thumb

Обратите внимание, что если посетитель напрямую запрашивает файл (обращается к папке archive), то сервер все равно передает управление движку сайта, который отправляет 404-ую ошибку.

О фреймворке CodeIgniter я уже неоднократно рассказывал, поэтому останавливаться на его настройке и принципах работы не буду. В этом примере фреймворк используется только для демонстрации, чтобы сократить объем кода, и вы можете спокойно заменить его любым другим.

Контроллер

<?php
/**
 * Управление скачиванием файлов
 *
 * @version 0.1
 * @link https://www.simplecoding.org
 * @author Стаценко Владимир <vova_33@gala.net>
 */
class Main extends Controller {

	function Main() {
		parent::Controller();
		$this->load->helper('file');
		$this->load->config('downloader');
	}

	/**
	 * Формирует главную страницу со списком файлов
	 */
	function index() {
		$pageData['title'] = 'Управление скачиванием файлов';
		$pageData['files'] = get_filenames($this->config->item('filesdir'));
		
		$this->load->view('header', $pageData);
		$this->load->view('filelist');
		$this->load->view('footer');
	}
	
	/**
	 * Формирует страницу с каптчей, которую должен 
	 * ввести посетитель чтобы скачать файл
	 *
	 * @param $fileName - имя файла, который пользователь хочет загрузить
	 */
	function confirm($fileName = '') {
		session_start();
		
		//если посетитель ввел каптчу
		if ($this->input->post('captcha')) {
			//проверяем ответ на вопрос
			if ((int)$this->input->post('captcha') === $_SESSION['captcha']) {
				//создаем временную ссылку
				//сохраняем её в базе
				$this->load->model('mfilelink');
				
				$fileLink = $this->mfilelink->createLink($fileName);
				//если ссылка создана и сохранена
				if ($fileLink) {
					//отправляем посетителя на страницу загрузки файла
					redirect('main/download/'.$fileLink, 'refresh');
					exit;
				}
				//если нет - создаем сообщение об ошибке и перенаправляем посетителя на страницу
				//со списком файлов
				else {
					$this->session->set_flashdata('error', 'Извините, этот файл не может быть загружен.');
					redirect('main/index', 'refresh');
					exit;
				}
			}
			//если посетитель неправильно ввел каптчу
			else {
				$this->session->set_flashdata('error', 'Вы неправильно ответили на вопрос. Попробуйте ещё раз.');
				redirect('main/confirm/'.$fileName, 'refresh');
				exit;
			}
		}
		
		//игнорируем запросы к несуществующим файлам
		if (!get_file_info($this->config->item('filesdir').'/'.$fileName)) {
			redirect('main/index', 'refresh');
			exit;
		}
		
		$this->load->helper('form');
		
		//подготавливаем данные для каптчи
		$x = mt_rand(1, 9);
		$y = mt_rand(1, 9);
		
		$_SESSION['captcha'] = $x + $y;
		
		//формируем страницу
		$pageData['title'] = 'Управление скачиванием файлов';
		$pageData['fileName'] = $fileName;
		$pageData['x'] = $x;
		$pageData['y'] = $y;
		
		$this->load->view('header', $pageData);
		$this->load->view('confirm');
		$this->load->view('footer');
	}
	
	/**
	 * Этот метод проверяет временную ссылку
	 *
	 * @param $fileLink - временная ссылка на файл
	 */
	function download($fileLink) {
		//проверка актуальности ссылки (по БД)
		$this->load->model('mfilelink');
		$fileName = $this->mfilelink->checkFileLink($fileLink);
		//если проверка прошла - отправляем файл
		if ($fileName) {
			$this->_sendFile($fileName);
		}
		else {
			//в протином случае - отправляем посетителя на страницу со списком файлов
			$this->session->set_flashdata('error', 'Извините, эта ссылка нерабочая.');
			redirect('main/index', 'refresh');
		}
	}
	
	/**
	 * Этот метод отправляет файл посетителю
	 * (вызывается из $this->download после проверки временной ссылки)
	 *
	 * @param $fileName имя файла
	 */
	function _sendFile($fileName) {
		$this->load->helper('download');
		//читаем содержимое файла
		$data = file_get_contents($this->config->item('filesdir').'/'.$fileName);
		//отправляем его посетителю
		force_download($fileName, $data);
	}
}
?>

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

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