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

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), и так далее. Исходя из этого, как правило, легче реализовать такую политику безопасности, в которой запрещено все, исключая то, что явно разрешено.

Коментарии

Well, the fact that all users run under the same UID is a big problem. Userspace  security hacks (ala safe_mode) should not be substitution for proper kernel level security checks/accounting.
Good news: Apache 2 allows you to assign UIDs for different vhosts.
devik
2001-08-21 10:52:48
http://php5.kiev.ua/manual/ru/security.filesystem.html
when using Apache you might consider a apache_lookup_uri on the path, to discover the real path, regardless of any directory trickery.
then, look at the prefix, and compare with a list of allowed prefixes.
for example, my source.php for my website includes:
if(isset($doc)) {
    $apacheres = apache_lookup_uri($doc);
    $really = realpath($apacheres->filename);
    if(substr($really, 0, strlen($DOCUMENT_ROOT)) == $DOCUMENT_ROOT) {
        if(is_file($really)) {
            show_source($really);
        }
    }
}
hope this helps
regards,
KAT44
2002-01-05 17:48:19
http://php5.kiev.ua/manual/ru/security.filesystem.html
All of the fixes here assume that it is necessary to allow the user to enter system sensitive information to begin with. The proper way to handle this would be to provide something like a numbered list of files to perform an unlink action on and then the chooses the matching number. There is no way for the user to specify a clever attack circumventing whatever pattern matching filename exclusion syntax that you may have.

Anytime you have a security issue, the proper behaviour is to deny all then allow specific instances, not allow all and restrict. For the simple reason that you may not think of every possible restriction.
2005-10-09 19:31:29
http://php5.kiev.ua/manual/ru/security.filesystem.html
Автор:
(A) Better not to create files or folders with user-supplied names. If you do not validate enough, you can have trouble. Instead create files and folders with randomly generated names like fg3754jk3h and store the username and this file or folder name in a table named, say, user_objects. This will ensure that whatever the user may type, the command going to the shell will contain values from a specific set only and no mischief can be done.

(B) The same applies to commands executed based on an operation that the user chooses. Better not to allow any part of the user's input to go to the command that you will execute. Instead, keep a fixed set of commands and based on what the user has input, and run those only. 

For example,
(A) Keep a table named, say, user_objects with values like:
username|chosen_name   |actual_name|file_or_dir
--------|--------------|-----------|-----------
jdoe    |trekphotos    |m5fg767h67 |D
jdoe    |notes.txt     |nm4b6jh756 |F
tim1997 |_imp_ folder  |45jkh64j56 |D

and always use the actual_name in the filesystem operations rather than the user supplied names.

(B)
<?php
$op 
$_POST['op'];//after a lot of validations 
$dir $_POST['dirname'];//after a lot of validations or maybe you can use technique (A)
switch($op){
    case 
"cd":
       
chdir($dir);
        break;
    case 
"rd":
       
rmdir($dir);
        break;
    .....
    default:
       
mail("webmaster@example.com""Mischief"$_SERVER['REMOTE_ADDR']." is probably attempting an attack.");
}
2005-11-17 04:58:52
http://php5.kiev.ua/manual/ru/security.filesystem.html
A basic filename/directory/symlink checking may be done (and I personally do) via realpath() ...

<?php

if (isset($_GET['file'])) {
   
$base '/home/polizei/public_html/'// it seems this one is good to be realpath too.. meaning not a symlinked path..
   
if (strpos($file realpath($base.$_GET['file']), $base) === && is_file($file)) {
       
unlink($file);
    } else {
        die(
'blah!');
    }
}
?>
2009-03-10 05:06:11
http://php5.kiev.ua/manual/ru/security.filesystem.html

    Поддержать сайт на родительском проекте КГБ