Безопасность файловой системы

PHP подчиняется установкам безопасности на уровне файлов и каталогов, которые встроены в большинство серверных систем. Это позволяет контролировать, какие файлы могут быть прочитаны. С особой аккуратностью нужно относиться к файлам с правом чтения "для всех" - нужно помнить о том, они могут быть считаны любым пользователем, имеющим доступ к данной файловой системе.

Поскольку PHP разрабатывался для предоставления пользовательских прав доступа к системе, возможно создать программу на PHP, которая позволит вам читать системные файлы, такие, как /etc/passwd, изменять параметры ваших сетевых соединений, создавать большие задания для печати и т.п. Это создает ряд проблем - нужно быть уверенным в том, что именно необходимые файлы разрешены для чтения и записи.

Приведем следующую программу, где пользователь хочет стереть файл в своем домашнем каталоге. Это типичная ситуация, когда Web-интерфейс PHP используется для управления файлами, т.е. пользователи Apache имеют возможность удалять файлы в своих домашних каталогах.

Пример 5-1. Недостаточная проверка переменных - последствия...

<?php
// удаляем файл из пользовательского домашнего каталога
$username = $_POST['user_submitted_name'];
$homedir = "/home/$username";
$file_to_delete = "$userfile";
unlink ($homedir/$userfile);
echo "$file_to_delete удален!";
?>
Поскольку имя пользователя предоставляется пользовательской формой ввода, можно подставить любые имена пользователя и файла, даже если этот файл принадлежит кому-то другому. В этом случае необходимо использовать схему аутентификации пользователей. Кроме того, представьте, что случится, если переданные переменные будут соответственно "../etc/" и "passwd". Тогда программа будет выглядеть так:

Пример 5-2. ... Атака на файловую систему

<?php
// удаляет любой файл в зоне действия прав пользователя
// Если PHP имеет права администратора:
$username = "../etc/";
$homedir = "/home/../etc/";
$file_to_delete = "passwd";
unlink ("/home/../etc/passwd");
echo "/home/../etc/passwd удален!";
?>
В борьбе с подобными случаями есть два ключевых момента.

Вот улучшенная программа:

Пример 5-3. Более безопасная проверка имени файла

<?php
// удаляет любой файл в зоне действия прав пользователя
// Если PHP имеет права администратора:
$username = $_SERVER['REMOTE_USER']; // используем механизм аутентификации

$homedir = "/home/$username";

$file_to_delete = basename("$userfile"); // отрезать пути
unlink ("$homedir/$file_to_delete");

$fp = fopen("/home/logging/filedelete.log","+a"); // отчитаться об удалении
$logstring = "$username $homedir $file_to_delete";
fputs ($fp, $logstring);
fclose($fp);

echo "$file_to_delete удален!";
?>
Однако и в этом случае имеются "дырки". Если ваша система аутентификации позволяет пользователям выбирать собственные имена, и пользователь задаст имя "../etc/", система опять попадает под атаку. В этом случае можно сделать более специфическую проверку:

Пример 5-4. Еще более безопасная проверка имени файла

<?php
$username = $_SERVER['REMOTE_USER']; // используем механизм аутентификации
$homedir = "/home/$username";

if (!ereg('^[^./][^/]*$', $userfile))
     die('плохое имя файла'); // выход без обработки

if (!ereg('^[^./][^/]*$', $username))
     die('плохое имя пользователя'); // выход без обработки
//и т.д.
?>

В зависимости от вашей операционной системы, существуют файлы, о безопасности которых надо позаботиться - драйверы устройств (/dev/ или COM1), файлы конфигурации (файлы /etc/ или файлы .ini), известные области хранения данных (/home/, Mои документы), и т.д. В связи с этим, обычно проще создать политику в которой запрещено все, кроме того, что разрешено, как говорится, "вручную".