<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/security.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'security.filesystem.php',
    1 => 'Безопасность файловой системы',
    2 => 'Безопасность файловой системы',
  ),
  'up' => 
  array (
    0 => 'security.php',
    1 => 'Безопасность',
  ),
  'prev' => 
  array (
    0 => 'security.sessions.php',
    1 => 'Безопасность сессий',
  ),
  'next' => 
  array (
    0 => 'security.filesystem.nullbytes.php',
    1 => 'Нулевые байты и безопасность',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'security/filesystem.xml',
  ),
  'history' => 
  array (
  ),
  'extra_header_links' => 
  array (
    'rel' => 'alternate',
    'href' => '/manual/en/feeds/security.filesystem.atom',
    'type' => 'application/atom+xml',
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="security.filesystem" class="chapter">
   <h1 class="title">Безопасность файловой системы</h1>
<h2>Содержание</h2><ul class="chunklist chunklist_chapter"><li><a href="security.filesystem.nullbytes.php">Нулевые байты и безопасность</a></li></ul>

   <p class="simpara">
    <abbr title="PHP: Hypertext Preprocessor">PHP</abbr> подчиняется правилам безопасности, которые встроены в бо́льшую часть
    серверных систем в отношении разрешений для файлов и каталогов.
    Следование правилам разрешает разработчика управлять тем, какие файлы
    в файловой системе доступны для чтения.
    При настройке файлов, доступ на чтение которых есть у мира,
    соблюдают осторожность, чтобы гарантировать, что файлы безопасны для чтения пользователями
    с доступом к файловой системе.
   </p>
   <p class="simpara">
    Поскольку <abbr title="PHP: Hypertext Preprocessor">PHP</abbr> разработали для доступа к файловой системе на уровне пользователя,
    можно написать <abbr title="PHP: Hypertext Preprocessor">PHP</abbr>-скрипт,
    который разрешит читать системные файлы наподобие <var class="filename">/etc/passwd</var>,
    изменять Ethernet-соединения, отправлять большие задания на печать и т. д.
    У этого есть ряд последствий, и поэтому нужно убедиться,
    что не возникла ошибка с выбором файла, который разработчик читает и в который записывает данные.
   </p>
   <p class="simpara">
    Рассмотрим следующий скрипт, в котором пользователь указывает,
    что хотел бы удалить файл из пользовательского домашнего каталога.
    Это предполагает, что управление файлами регулярно использует
    веб-интерфейс <abbr title="PHP: Hypertext Preprocessor">PHP</abbr>,
    поэтому пользователю веб-сервера Apache разрешается удалять файлы
    в домашних каталогах пользователя.
   </p>
   <p class="para">
    <div class="example" id="example-1">
     <p><strong>Пример #1 Недостаточная проверка переменных приводит к…</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// Удаление файла из домашней директории пользователя<br /></span><span style="color: #0000BB">$username </span><span style="color: #007700">= </span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_name'</span><span style="color: #007700">];<br /></span><span style="color: #0000BB">$userfile </span><span style="color: #007700">= </span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_filename'</span><span style="color: #007700">];<br /></span><span style="color: #0000BB">$homedir  </span><span style="color: #007700">= </span><span style="color: #DD0000">"/home/</span><span style="color: #0000BB">$username</span><span style="color: #DD0000">"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">unlink</span><span style="color: #007700">(</span><span style="color: #DD0000">"</span><span style="color: #0000BB">$homedir</span><span style="color: #DD0000">/</span><span style="color: #0000BB">$userfile</span><span style="color: #DD0000">"</span><span style="color: #007700">);<br /><br />echo </span><span style="color: #DD0000">"Скрипт удалил файл!"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
    Поскольку имя пользователя и название файла приходят
    из пользовательской формы, не исключается риск подмены и удаления данных,
    которые принадлежат другому пользователю, даже если у пользователя не было разрешения
    на удаление данных. Тогда требуется аутентификация.
    Посмотрим, что произойдёт, если отправить значения
    <code class="literal">&quot;../etc/&quot;</code> и <code class="literal">&quot;passwd&quot;</code>. Тогда код будет выглядеть вот так:
    <div class="example" id="example-2">
     <p><strong>Пример #2 …атаке на файловую систему</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// Удаляем файл из произвольного места на жестком диске,<br />// к которому у пользователя PHP-скрипта есть доступ. Если PHP работает с правами суперпользователя:<br /></span><span style="color: #0000BB">$username </span><span style="color: #007700">= </span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_name'</span><span style="color: #007700">]; </span><span style="color: #FF8000">// В переменной передали значение "../etc"<br /></span><span style="color: #0000BB">$userfile </span><span style="color: #007700">= </span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_filename'</span><span style="color: #007700">]; </span><span style="color: #FF8000">// В переменной передали значение "passwd"<br /></span><span style="color: #0000BB">$homedir  </span><span style="color: #007700">= </span><span style="color: #DD0000">"/home/</span><span style="color: #0000BB">$username</span><span style="color: #DD0000">"</span><span style="color: #007700">; </span><span style="color: #FF8000">// "/home/../etc"<br /><br /></span><span style="color: #0000BB">unlink</span><span style="color: #007700">(</span><span style="color: #DD0000">"</span><span style="color: #0000BB">$homedir</span><span style="color: #DD0000">/</span><span style="color: #0000BB">$userfile</span><span style="color: #DD0000">"</span><span style="color: #007700">); </span><span style="color: #FF8000">// "/home/../etc/passwd"<br /><br /></span><span style="color: #007700">echo </span><span style="color: #DD0000">"Скрипт удалил файл!"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
    Атаки предотвращают двумя способами.
    <ul class="itemizedlist">
     <li class="listitem">
      <span class="simpara">
       Ограничивают права доступа на двоичный файл веб-пользователя <abbr title="PHP: Hypertext Preprocessor">PHP</abbr>.
      </span>
     </li>
     <li class="listitem">
      <span class="simpara">
       Проверяют каждую переменную, которую передают пользователи.
      </span>
     </li>
    </ul>
    Вот улучшенный вариант кода:
    <div class="example" id="example-3">
     <p><strong>Пример #3 Более безопасная проверка имени файла</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">// Удаляем файл из произвольного места на жестком диске,<br />// к которому у пользователя PHP-скрипта есть доступ.<br /></span><span style="color: #0000BB">$username </span><span style="color: #007700">= </span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'REMOTE_USER'</span><span style="color: #007700">]; </span><span style="color: #FF8000">// Проверяем, прошёл ли пользователь аутентификацию<br /></span><span style="color: #0000BB">$userfile </span><span style="color: #007700">= </span><span style="color: #0000BB">basename</span><span style="color: #007700">(</span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_filename'</span><span style="color: #007700">]);<br /></span><span style="color: #0000BB">$homedir  </span><span style="color: #007700">= </span><span style="color: #DD0000">"/home/</span><span style="color: #0000BB">$username</span><span style="color: #DD0000">"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$filepath </span><span style="color: #007700">= </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$homedir</span><span style="color: #DD0000">/</span><span style="color: #0000BB">$userfile</span><span style="color: #DD0000">"</span><span style="color: #007700">;<br /><br />if (</span><span style="color: #0000BB">file_exists</span><span style="color: #007700">(</span><span style="color: #0000BB">$filepath</span><span style="color: #007700">) &amp;&amp; </span><span style="color: #0000BB">unlink</span><span style="color: #007700">(</span><span style="color: #0000BB">$filepath</span><span style="color: #007700">)) {<br />    </span><span style="color: #0000BB">$logstring </span><span style="color: #007700">= </span><span style="color: #DD0000">"Функция удалила файл </span><span style="color: #0000BB">$filepath</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />} else {<br />    </span><span style="color: #0000BB">$logstring </span><span style="color: #007700">= </span><span style="color: #DD0000">"Не удалось удалить файл </span><span style="color: #0000BB">$filepath</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">$fp </span><span style="color: #007700">= </span><span style="color: #0000BB">fopen</span><span style="color: #007700">(</span><span style="color: #DD0000">"/home/logging/filedelete.log"</span><span style="color: #007700">, </span><span style="color: #DD0000">"a"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">fwrite</span><span style="color: #007700">(</span><span style="color: #0000BB">$fp</span><span style="color: #007700">, </span><span style="color: #0000BB">$logstring</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">fclose</span><span style="color: #007700">(</span><span style="color: #0000BB">$fp</span><span style="color: #007700">);<br /><br />echo </span><span style="color: #0000BB">htmlentities</span><span style="color: #007700">(</span><span style="color: #0000BB">$logstring</span><span style="color: #007700">, </span><span style="color: #0000BB">ENT_QUOTES</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
    Однако даже такая проверка не лишена недостатков. Если
    система аутентификации разрешает пользователям создавать произвольные логины,
    и взломщик выбрал логин <code class="literal">&quot;../etc/&quot;</code>, система снова становится уязвимой.
    Поэтому предпочитают более строгую проверку:
    <div class="example" id="example-4">
     <p><strong>Пример #4 Более строгая проверка имени файла</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$username     </span><span style="color: #007700">= </span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'REMOTE_USER'</span><span style="color: #007700">]; </span><span style="color: #FF8000">// Проверяем, прошёл ли пользователь аутентификацию<br /></span><span style="color: #0000BB">$userfile     </span><span style="color: #007700">= </span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_submitted_filename'</span><span style="color: #007700">];<br /></span><span style="color: #0000BB">$homedir      </span><span style="color: #007700">= </span><span style="color: #DD0000">"/home/</span><span style="color: #0000BB">$username</span><span style="color: #DD0000">"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">$filepath     </span><span style="color: #007700">= </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$homedir</span><span style="color: #DD0000">/</span><span style="color: #0000BB">$userfile</span><span style="color: #DD0000">"</span><span style="color: #007700">;<br /><br />if (!</span><span style="color: #0000BB">ctype_alnum</span><span style="color: #007700">(</span><span style="color: #0000BB">$username</span><span style="color: #007700">) || !</span><span style="color: #0000BB">preg_match</span><span style="color: #007700">(</span><span style="color: #DD0000">'/^(?:[a-z0-9_-]|\.(?!\.))+$/iD'</span><span style="color: #007700">, </span><span style="color: #0000BB">$userfile</span><span style="color: #007700">)) {<br />    die(</span><span style="color: #DD0000">"Неправильное имя пользователя или файл"</span><span style="color: #007700">);<br />}<br /><br /></span><span style="color: #FF8000">// и т. д.<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
   </p>
   <p class="para">
    Набор файлов, за которыми придётся следить разработчику,
    определяет операционная система, и включает
    системные файлы устройств <var class="filename">/dev/</var> или <var class="filename">COM1</var>, конфигурационные файлы
    <var class="filename">/etc/</var> и файлы с расширением <code class="literal">.ini</code>, хорошо известные
    области хранения файлов <var class="filename">/home/</var>, <var class="filename">Мои документы</var> и так далее.
    Поэтому обычно проще создать политику безопасности, которая запрещает
    всё, кроме того, что явно разрешили.
   </p>
   

  </div>
<?php manual_footer($setup); ?>