1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-26 00:32:15 +01:00
Files
archived-doc-ru/security/filesystem.xml
Sergey Panteleev daef8df962 Обнуление тега Reviewed (#364)
[skip-lint]
[skip-spellcheck]
2021-11-16 13:03:53 +03:00

227 lines
10 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: c5d92fd7127e059d448d43ba339f19956f83b05a Maintainer: shein Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
<title>Безопасность файловой системы</title>
<simpara>
<acronym>PHP</acronym> является одним из важных моментов в вопросе безопасности сервера,
поскольку PHP-скрипты могут манипулировать файлами и каталогами
на диске. В связи с этим существуют конфигурационные настройки,
указывающие, какие файлы могут быть доступны и какие операции с ними можно
выполнять. Необходимо проявлять осторожность, поскольку любой из файлов,
с полными правами чтения ("world readable") может быть прочитан
каждым, кто имеет доступ к файловой системе.
</simpara>
<simpara>
Поскольку в <acronym>PHP</acronym> изначально предполагался полноправный пользовательский
доступ к файловой системе, можно написать <acronym>PHP</acronym>-скрипт,
который позволит читать системные файлы, такие как /etc/passwd,
управлять сетевыми соединениями, отправлять задания принтеру, и
так далее. Как следствие, вы всегда должны быть уверены в том, что
файлы, которые вы читаете или модифицируете, являются именно теми,
которые вы подразумевали.
</simpara>
<simpara>
Рассмотрим следующий пример, в котором пользователь создал скрипт, удаляющий
файл из его домашней директории. Предполагается ситуация, когда веб-интерфейс,
написанный на <acronym>PHP</acronym>, регулярно используется для
работы с файлами, и настройки безопасности позволяют удалять
файлы в домашнем каталоге.
</simpara>
<para>
<example>
<title>Недостаточная проверка внешних данных ведёт к...</title>
<programlisting role="php">
<![CDATA[
<?php
// Удаление файла из домашней директории пользователя
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "Файл был удалён!";
?>
]]>
</programlisting>
</example>
Поскольку переменные вводятся в пользовательской форме, существует
возможность удалить файлы, принадлежащие кому-либо другому, введя
соответствующие значения. В этом случае может понадобиться авторизация.
Посмотрим, что произойдёт, если будут отправлены значения
"../etc/" и "passwd". Скрипт выполнит следующие действия:
<example>
<title>... атаке на файловую систему</title>
<programlisting role="php">
<![CDATA[
<?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 "Файл был удалён!";
?>
]]>
</programlisting>
</example>
Существуют две важные меры, которые можно предпринять для предотвращения
описанной проблемы.
<itemizedlist>
<listitem>
<simpara>
Ограничить доступ пользователя, с правами которого работает веб-сервер
с <acronym>PHP</acronym>.
</simpara>
</listitem>
<listitem>
<simpara>
Проверять все данные, вводимые пользователем.
</simpara>
</listitem>
</itemizedlist>
Вот улучшенный вариант кода:
<example>
<title>Более безопасная проверка имени файла</title>
<programlisting role="php">
<![CDATA[
<?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);
?>
]]>
</programlisting>
</example>
Однако и такая проверка не учитывает все возможные ситуации. Если
система авторизации позволяет пользователям выбирать произвольные логины,
взломщик может создать учётную запись вида "../etc/" и система опять
окажется уязвимой. Исходя из этого, вам может понадобиться более строгая проверка:
<example>
<title>Более строгая проверка имени файла</title>
<programlisting role="php">
<![CDATA[
<?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...
?>
]]>
</programlisting>
</example>
</para>
<para>
В зависимости от используемой вами операционной системы необходимо
предусматривать возможность атаки на разнообразные файлы, включая
системные файлы устройств (/dev/ или COM1), конфигурационные файлы
(например /etc/ или файлы с расширением .ini), хорошо известные
области хранения данных (/home/, My Documents), и так далее.
Исходя из этого, как правило, легче реализовать такую политику
безопасности, в которой запрещено все, исключая то, что явно
разрешено.
</para>
<sect1 xml:id="security.filesystem.nullbytes">
<title>Проблемы безопасности, связанные с нулевым байтом</title>
<simpara>
Так как для работы с файловой системой <acronym>PHP</acronym>
использует нижележащие C-функции, то в этом случае возможна
крайне неожиданная обработка нулевого байта.
Так как нулевой байт означает конец строки в C, то строки, содержащие
такой байт, не будут трактоваться полностью, а только до той
позиции, в которой находится этот байт.
Следующий пример содержит уязвимый код, демонстрирующий эту проблему:
</simpara>
<example>
<title>Скрипт, уязвимый к нулевому байту</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/'.$file.'.php')) {
// file_exists возвратит true, т.к. /home/wwwrun/../../etc/passwd существует
include '/home/wwwrun/'.$file.'.php';
// будет подключён файл /etc/passwd
}
?>
]]>
</programlisting>
</example>
<para>
Таким образом, любая испорченная строка, используемая в операциях
с файловой системой должна быть соответствующим образом проверена.
Вот улучшенная версия предыдущего примера:
</para>
<example>
<title>Корректная проверка входных данных</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file'];
// Белый список возможных значений
switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/'.$file.'.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>
]]>
</programlisting>
</example>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->