PHP подчиняется правилам безопасности, которые встроены в бо́льшую часть серверных систем в отношении разрешений для файлов и каталогов. Следование правилам разрешает разработчика управлять тем, какие файлы в файловой системе доступны для чтения. При настройке файлов, доступ на чтение которых есть у мира, соблюдают осторожность, чтобы гарантировать, что файлы безопасны для чтения пользователями с доступом к файловой системе.
Поскольку PHP разработали для доступа к файловой системе на уровне пользователя, можно написать PHP-скрипт, который разрешит читать системные файлы наподобие /etc/passwd, изменять Ethernet-соединения, отправлять большие задания на печать и т. д. У этого есть ряд последствий, и поэтому нужно убедиться, что не возникла ошибка с выбором файла, который разработчик читает и в который записывает данные.
Рассмотрим следующий скрипт, в котором пользователь указывает, что хотел бы удалить файл из пользовательского домашнего каталога. Это предполагает, что управление файлами регулярно использует веб-интерфейс PHP, поэтому пользователю веб-сервера Apache разрешается удалять файлы в домашних каталогах пользователя.
Пример #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 работает с правами суперпользователя:
$username = $_POST['user_submitted_name']; // В переменной передали значение "../etc"
$userfile = $_POST['user_submitted_filename']; // В переменной передали значение "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "Скрипт удалил файл!";
?>
Пример #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("Неправильное имя пользователя или файл");
}
// и т. д.
?>
Набор файлов, за которыми придётся следить разработчику,
определяет операционная система, и включает
системные файлы устройств /dev/ или COM1, конфигурационные файлы
/etc/ и файлы с расширением .ini
, хорошо известные
области хранения файлов /home/, Мои документы и так далее.
Поэтому обычно проще создать политику безопасности, которая запрещает
всё, кроме того, что явно разрешили.