Безопасность файловой системы
PHP является одним из важных моментов в вопросе безопасности сервера,
поскольку PHP-скрипты могут манипулировать файлами и каталогами
на диске. В связи с этим существуют конфигурационные настройки,
указывающие, какие файлы могут быть доступны и какие операции с ними можно
выполнять. Необходимо проявлять осторожность, поскольку любой из файлов с
соответствующими правами доступа может быть прочитан каждым, кто имеет доступ
к файловой системе.
Поскольку в PHP изначально предполагался полноправный пользовательский
доступ к файловой системе, можно написать скрипт, который позволит читать
системные файлы, такие как /etc/passwd, управлять сетевыми соединениями,
отправлять задания принтеру, и так далее. Как следствие вы всегда должны быть
уверены в том, что файлы, которые вы читаете или модифицируете, соответствуют вашим
намерениям.
Рассмотрим следующий пример, в коротом пользователь создал скрипт, удаляющий
файл из его домашней директории. Предполагается ситуация, когда веб-интерфейс,
написанный на PHP, регулярно используется для работы с файлами, и настройки
безопасности позволяют удалять файлы в домашнем каталоге.
Пример #1 Недостаточная проверка внешних данных.
<?php
// Удаление файла из домашней директории пользователя
$username = $_POST['user_submitted_name'];
$homedir = "/home/$username";
$file_to_delete = "$userfile";
unlink ("$homedir/$userfile");
echo "$file_to_delete has been deleted!";
?>
Поскольку переменные вводятся в пользовательской форме, существует
возможность удалить файлы, принадлежащие кому-либо другому, введя
соответствующие значения. В этом случае может понадобиться авторизация.
Посмотрим, что произойдет, если будут отправлены значения
"../etc/" и "passwd". Скрипт выполнит следующие действия:
Пример #2 Атака на файловую систему
<?php
// Удаление любого файла, доступного из PHP-скрипта.
// В случае, если PHP работает с правами пользователя root:
$username = "../etc/";
$homedir = "/home/../etc/";
$file_to_delete = "passwd";
unlink ("/home/../etc/passwd");
echo "/home/../etc/passwd has been deleted!";
?>
Cуществуют два решения описанной проблемы.
-
Ограничить доступ пользователя, с правами которого работает веб-сервер.
-
Проверять все данные, вводимые пользователем.
Вот улучшеный вариант кода:
Пример #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";
fwrite ($fp, $logstring);
fclose($fp);
echo "$file_to_delete has been deleted!";
?>
Однако и такая проверка не учитывает все возможные ситуации. Если
система авторизации позволяет пользователям выбирать произвольные логины,
вломщик может создать учетную запись вида "../etc/" и система опять
окажется уязвимой. Исходя из этого, вам может понадобиться более строгая проверка:
Пример #4 Более строгая проверка имени файла
<?php
$username = $_SERVER['REMOTE_USER']; // использование авторизации
$homedir = "/home/$username";
if (!ereg('^[^./][^/]*$', $userfile))
die('bad filename'); //завершение работы
if (!ereg('^[^./][^/]*$', $username))
die('bad username'); //завершение работы
//etc...
?>
В зависимости от используемой вами операционной системы необходимо
предусматривать возможность атаки на разнообразные файлы, включая
системные файлы устройств (/dev/ или COM1), конфигурационные файлы
(например /etc/ или файлы с расширением .ini), хорошо известные области хранения данных
(/home/, My Documents), и так далее. Исходя из этого, как правило, легче
реализовать такую политику безопасности, в которой запрещено все, исключая
то, что явно разрешено.