Безопасность файловой системы
Содержание
PHP является одним из важных моментов в вопросе безопасности сервера,
поскольку PHP-скрипты могут манипулировать файлами и каталогами
на диске. В связи с этим существуют конфигурационные настройки,
указывающие, какие файлы могут быть доступны и какие операции с ними можно
выполнять. Необходимо проявлять осторожность, поскольку любой из файлов,
с полными правами чтения ("world readable") может быть прочитан
каждым, кто имеет доступ к файловой системе.
Поскольку в PHP изначально предполагался полноправный пользовательский
доступ к файловой системе, можно написать PHP-скрипт,
который позволит читать системные файлы, такие как /etc/passwd,
управлять сетевыми соединениями, отправлять задания принтеру, и
так далее. Как следствие, вы всегда должны быть уверены в том, что
файлы, которые вы читаете или модифицируете, являются именно теми,
которые вы подразумевали.
Рассмотрим следующий пример, в котором пользователь создал скрипт, удаляющий
файл из его домашней директории. Предполагается ситуация, когда веб-интерфейс,
написанный на PHP, регулярно используется для
работы с файлами, и настройки безопасности позволяют удалять
файлы в домашнем каталоге.
Пример #1 Недостаточная проверка внешних данных ведёт к...
<?php
// Удаление файла из домашней директории пользователя
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "Файл был удалён!";
?>
Поскольку переменные вводятся в пользовательской форме, существует
возможность удалить файлы, принадлежащие кому-либо другому, введя
соответствующие значения. В этом случае может понадобиться авторизация.
Посмотрим, что произойдёт, если будут отправлены значения
"../etc/" и "passwd". Скрипт выполнит следующие действия:
Пример #2 ... атаке на файловую систему
<?php
// Удаление любого файла, доступного из PHP-скрипта.
// В случае, если PHP работает с правами пользователя root:
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "Файл был удалён!";
?>
Существуют две важные меры, которые можно предпринять для предотвращения
описанной проблемы.
-
Ограничить доступ пользователя, с правами которого работает веб-сервер
с PHP.
-
Проверять все данные, вводимые пользователем.
Вот улучшенный вариант кода:
Пример #3 Более безопасная проверка имени файла
<?php
// Удаление любого файла, к которому имеет доступ пользователь,
// под которым запущен PHP.
$username = $_SERVER['REMOTE_USER']; // использование авторизации
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "$filepath удалён\n";
} else {
$logstring = "Не удалось удалить $filepath\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
Однако и такая проверка не учитывает все возможные ситуации. Если
система авторизации позволяет пользователям выбирать произвольные логины,
взломщик может создать учётную запись вида "../etc/" и система опять
окажется уязвимой. Исходя из этого, вам может понадобиться более строгая проверка:
Пример #4 Более строгая проверка имени файла
<?php
$username = $_SERVER['REMOTE_USER']; // использование авторизации
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Неправильное имя пользователя или файл");
}
//etc...
?>
В зависимости от используемой вами операционной системы необходимо
предусматривать возможность атаки на разнообразные файлы, включая
системные файлы устройств (/dev/ или COM1), конфигурационные файлы
(например /etc/ или файлы с расширением .ini), хорошо известные
области хранения данных (/home/, My Documents), и так далее.
Исходя из этого, как правило, легче реализовать такую политику
безопасности, в которой запрещено все, исключая то, что явно
разрешено.