Безопасность средствами суперглобальных массивов в PHP

Автор: David Lechnyr
Перевод: Андрей Киселев

"Избегайте странных женщин и временных переменных." -- Anonymous

Пару лет тому назад, мы с супругой решили покататься на лыжах. Чтобы забронировать необходимую экипировку, необходимо было подать заявку на сайте фирмы, дающей все необходимое на прокат, не позднее чем за сутки. Вся беда была в том, что эта идея пришла к нам в голову за 23 часа до нужного срока.

Поэтому мне пришлось крепко призадуматься и заняться исследованием сайта, который не позволял забронировать все что нам нужно менее чем за 24 часа до нужного срока. Когда я выбрал текущую дату, то заметил, что URL приобрел такой вид:

https://www.somewhere.com/reservations.php?date=01-23-01

Я понял, что меня ограничили при выборе минимальной даты, но при этом запрашиваемую дату они поместили в инструкцию GET, в конце адресной строки. Я вручную изменил адрес так, чтобы он указывал на требуемую мне дату -- "date=01-22-01" и в результате -- на следующее утро наши лыжи уже ждали нас!

Этот довольно невинный пример достаточно ярко демонстрирует одну из опасностей, о которых мы должны знать при создании программ на любом языке программирования. Данная опасность состоит в том, что программа может быть использована способами не предусмотренными заранее. А теперь, после столь краткого вступления, можно перейти к обсуждению суперглобальных массивов в PHP.

Формы

Для того, чтобы понять почему так важны суперглобальные массивы (Superglobals) в PHP нам необходимо разобраться с тем, как передаются данные от одной web-страницы к другой. Определенно, вы уже должны знать, по крайней мере, о двух способах передачи данных, известных как GET и POST. Так же вы, скорее всего, уже должны быть знакомы с тегом <FORM> языка разметки HTML (загляните на http://www.w3.org/TR/html401/interact/forms.html).

Вероятно вы уже видели нечто подобное и раньше:

<form name="form1" method="post" action="process.php">
<p>Please enter your name:</p>
<p><input type="text" name="yourname" /></p>
<p><input type="button" name="Submit" value="Submit" /></p>
</form>

Это обычный код формы HTML, не представляющий из себя ничего необычного. Он запрашивает некоторые сведения у пользователя и затем передает полученные данные файлу (сценарию на языке PHP) "process.php" . Критическая точка здесь -- это объявление метода, которым будут переданы данные:

Если кто-то из читателей застал эпоху появления HTML на свет, тот наверняка вспомнит, что в те времена формы в HTML отсутствовали, а для того, чтобы запросить данные, приходилось вставлять тег <ISINDEX> в разделе <HEAD>. По мере развития, в HTML появился новый тег -- <FORM>, которому в атрибуте METHOD стало возможным указать метод передачи GET, POST или PUT. Теперь познакомимся с этими методами поближе.

GET

Метод GET, передает имена переменных и их значения через заголовок URL (адреса), добавляя их в конец URL. Такой метод передачи данных ограничен максимально возможной длиной строки URL, которая составляет 8192 символа. Если объем данных превышает это значение, то все что находится за пределами верхней границы будет попросту потеряно. Более того, даже на SSL соединениях данные не шифруются, поскольку они являются частью web-адреса.

Ниже приводится пример формы, которая могла бы быть размещена на web-страничке:

<form name="form1" method="get" action="process.php">
<p>Пожалуйста, укажите Ваше имя, адрес электронной почты, и примечания:</p>
<p><input type="text" name="yourname" /></p>
<p><input type="text" name="email" /></p>
<p><input type="text" name="comment" /></p>
<p><input type="button" name="Submit" value="Отправить" /></p>
</form>

Когда пользователь щелкает по кнопке Отправить, то браузер забирает значения из полей формы и отправляет их по адресу:

http://www.fluffygerbil.com/process.php?yourname=fred+smith&email=fred@nowhere.com&comment=I+have+no+comment

Заметили как значения, введенные в форму, стали частью адреса? В этом и состоит суть метода GET.

Для любопытных ниже приводится содержимое HTTP-запроса:

GET /process.php?yourname=fred+smith&email=fred@nowhere.com&comment=I+have+no+comment HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, */*
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)
Host: www.fluffygerbils.com
Connection: keep-alive

POST

Метод POST, передает имена переменных и их значения в теле запроса URL, а не в заголовке. Преимущества такого способа передачи данных состоят в том, что снимаются ограничения на объем передаваемых данных, поскольку они располагаются в теле запроса HTTP, а не в заголовке. Дополнительно, если вами используется SSL соединение, то данные проходят по сети в зашифрованном виде. :) Ниже приводится пример формы, использующей метод POST для передачи данных:

<form name="form1" method="post" action="process.php">
<p>Пожалуйста, укажите Ваше имя, адрес электронной почты, и примечания:</p>
<p><input type="text" name="yourname" /></p>
<p><input type="text" name="email" /></p>
<p><input type="text" name="comment" /></p>
<p><input type="button" name="Submit" value="Отправить" /></p>
</form>

Когда пользователь щелкает по кнопке Отправить, то браузер забирает значения из полей формы и отправляет их по адресу:

http://www.fluffygerbil.com/process.php

Заметили, что теперь данные уже не являются частью адреса? В этом и состоит суть метода POST.

Для любопытных ниже приводится содержимое HTTP-запроса:

POST /process.php HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, */*
Accept-Language: en-us
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)
Host: www.fluffygerbils.com
Content-Length: 94
Pragma: no-cache
Connection: keep-alive

yourname=fred+smith
email=fred@nowhere.com
comment=I+have+no+comment

Что дальше?

Итак, какая же нам польза от этих сведений? Возможно вы уже обращали внимание на появляющееся во время установки PHP такое вот сообщение:

    +--------------------------------------------------------------------+
    |                          *** NOTE ***                              |
    |            The default for register_globals is now OFF!            |
    |                                                                    |
    | If your application relies on register_globals being ON, you       |
    | should explicitly set it to on in your php.ini file.               |
    | Note that you are strongly encouraged to read                      |
    | http://www.php.net/manual/en/security.registerglobals.php          |
    | about the implications of having register_globals set to on, and   |
    | avoid using it if possible.                                        |
    +--------------------------------------------------------------------+
  
    +--------------------------------------------------------------------+
    |                        *** ВНИМАНИЕ ***                            |
    |          Теперь, по-умолчанию register_globals отключены!          |
    |                                                                    |
    | Если ваши приложения требуют наличия этой возможности, то вы       |
    | должны явно включить ее в вашем файле php.ini.                     |
    | Настоятельно рекомендуем вам прочитать                             |
    | http://www.php.net/manual/en/security.registerglobals.php          |
    | о назначении register_globals и избегать их использования          |
    | настолько, насколько это возможно.                                 |
    +--------------------------------------------------------------------+
  
Это говорит о том, что PHP проявляет крайне параноидальную озабоченность по поводу сохранности передаваемых ему данных и требует, чтобы вы точно определили метод передачи данных. Кроме того вы должны знать, что кроме GET и POST существуют и другие методы передачи данных web-страничкам:



Суперглобальные массивы в PHP (Superglobals)

Суперглобальные массивы -- это относительно новая идея в PHP. Внимательно рассмотрите диаграмму, представленную выше. Представьте, к примеру, что вы работаете с переменной $yourname, а теперь скажите -- можете ли вы быть абсолютно уверены в том, что в ходе исполнения сценария эта переменная не была переопределена одним из приведенных шести способов, кем либо, пытающимся взломать сценарий? Например, вообразите себе, что некто сумел закачать на ваш WEB-сервер со следующим содержанием (php exploit от Daniel Phoenix):

<?php
setcookie("test","../../../../../../etc/passwd");
echo "cookie inserted";
?>

Было бы великолепно иметь возможность изолировать переменные, исходя из того, каким методом они могут быть изменены. Суперглобальные массивы позволят точно определить каким способом переменная может получить свое значение.

Суперглобальные массивы -- это попытка PHP помочь вам определить -- каким образом переменная получила свое значение. Если вы ничего ранее не слышали об этой новой особенности PHP, которая появилась в версии 4.1.0, то теперь вы наверняка захотите использовать ее. Большая часть руководств по языку PHP совершенно не касается этой темы, поэтому вам необходимо знать о том, как перейти к использованию superglobals. В общем, вам надо открыть ваш файл /usr/local/lib/php.ini и внести следующие изменения:

register_globals = Off

Это предотвратит возможность изменения переменных сценария со стороны пользователя и тем самым ограничить возможности вероятных злоумышленников. Они будут вынуждены потратить значительное время, чтобы попытаться изобрести что-то новое, в то время как ваши внутренние переменные будут надежно изолированы от данных, поступающих извне. Если пользователь заполнит форму (упоминавшуюся выше), то сервер запомнит введенные данные не в глобальных переменных $name, $email или $comment, а в хэш-массиве:

$_POST['name']
$_POST['email']
$_POST['comment']

Основные суперглобальные массивы:

  1. $_GET['variable'] - Переменные, переданные сценарию через HTTP GET. Аналогичен массиву $HTTP_GET_VARS
  2. $_POST['variable'] - Переменные, переданные сценарию через HTTP POST. Аналогичен массиву $HTTP_POST_VARS

Другие суперглобальные массивы:

  1. $_COOKIE['variable'] - Переменные, переданные сценарию через HTTP cookies. Аналогичен массиву $HTTP_COOKIE_VARS
  2. $_REQUEST['variable'] - Переменные, переданные сценарию в результате ответа пользователя на запрос (GET, POST, COOKIE) и которым, поэтому, не следует доверять.
  3. $_GLOBALS['variable'] - Содержит ссылки на все переменные, которые в настоящий момент доступны в глобальной области видимости сценария. Ключами этого массива являются имена глобальных переменных.
  4. $_SERVER['variable'] - Переменные, установленные web-сервером или чем-то другим, напрямую связанным со средой исполнения сценария. Аналогичен массиву $HTTP_SERVER_VARS
  5. $_FILES['variable'] - Переменные, переданные сценарию путем передачи файла через HTTP. Аналогичен массиву $HTTP_POST_FILES
  6. $_ENV['variable'] - Переменные, переданные сценарию через окружение. Аналогичен массиву $HTTP_ENV_VARS
  7. $_SESSION['variable'] - Переменные, которые определены в текущей сессии сценария. Аналогичен массиву $HTTP_SESSION_VARS

Дополнительные сведения вы найдете в http://www.php.net/manual/en/reserved.variables.php.

Таким образом, вместо переменной $name, в которую записано, к примеру, имя "John", вы получите или $_GET['name'] = "John" или $_POST['name'] = "John", в зависимости от того, каким способом была передана эта переменная. Преимущество состоит в том, что:

  1. Переменная $name никогда не сможет быть изменена внешним пользователем; если сценарий записал туда некое значение, то оно там и останется!
  2. Массивы $_GET и $_POST помогут вам отделить данные, пришедшие от формы методом POST (в теле запроса) от данных, добавленных кем-либо, пытающимся имитировать передачу данных методом GET, в конец адресной строки. И это здорово...
  3. Суперглобальные массивы позволят вам не только изолировать значения переменных, но и разнести их по категориям, в зависимости от способа доставки на сервер. А кое-кому значительно усложнят взлом вашего сервера.

Заключение

Еще не так давно программирование на PHP могло доставить огорчения. Меры безопасности препятствуют возможности подмены значений переменных. Провайдеры услуг в Интернет (ISP), предоставляющие в распоряжение пользователей возможности PHP, обычно не учитывают уровень подготовки своей аудитории и озадачивают новичков в PHP такими понятиями, как GET, POST, Superglobals и так далее. Однако, недостаток знаний может сподвигнуть на долгий путь и я надеюсь, что эта статья помогла вам в ваших поисках.

Этот документ был написан, основываясь на PHP 4.3.0.

Дополнительные ресурсы

Этот документ был любовно набран на Dell Latitude C400 laptop, работающем под управлением Slackware Linux 8.1.

David работает сетевым администратором в отделе Человеческих Ресурсов, в университете штата Орегон. Он обладает степенью Магистра в сфере социальных проблем, а также имеет сертификаты MCSE+I, CNE и CCNA. С ОС Linux работает на протяжении последних шести лет, занимаясь в основном проблемами безопасности, обслуживанием сети, и интеграцией PHP/MySQL.


Copyright (C) 2003, David Lechnyr. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 86 of Linux Gazette, January 2003

Вернуться на главную страницу