Основы PHP
  Что такое PHP?
  Возможности PHP
  Преимущества PHP
  История развития
  Что нового в PHP5?
  «Движок» PHP
  Переход на PHP 5.3
New Переход на PHP 5.6
  Введение в PHP
  Изучение PHP
  Основы CGI
  Синтаксис PHP
  Типы данных PHP
  Переменные в PHP
  Константы PHP
  Выражения PHP
  Операторы PHP
  Конструкции PHP
  Ссылки в PHP
  PHP и ООП
  Безопасность
  Функции PHP
  Функции по категориям
  Функции по алфавиту
  Стандартные функции
  Пользовательские
  PHP и HTTP
  Работа с формами
  PHP и Upload
  PHP и Cookies
  PHP и базы данных
  PHP и MySQL
  Документация MySQL
  Учебники
  Учебники по PHP
  Учебники по MySQL
  Другие учебники
  Уроки PHP
  Введение
  Самые основы
  Управление
  Функции
  Документация
  Математика
  Файлы
  Основы SQL
  Дата и время
  CURL
  Изображения
  Стили
  Безопасность
  Установка
  Проектирование БД
  Регулярные выражения
  Подготовка к работе
  Быстрый старт
  Установка PHP
  Установка MySQL
  Конфигурация PHP
  Download / Скачать
  Скачать Apache
  Скачать PHP
  Скачать PECL
  Скачать PEAR
  Скачать MySQL
  Редакторы PHP
  Полезные утилиты
  Документация
  PHP скрипты
  Скачать скрипты
  Инструменты
  PHP в примерах
  Новости портала
 Главная   »  Сборник статей
 
 

Безопасность в PHP, Часть III

Автор: John Coggeshall
Автор перевода: Данил Миронов

Источник: detail.phpclub.net

  < Назад (Часть II)  

Добро пожаловать к нам опять, в цикл статей по основам PHP. В прошлый раз, я распространялся о потенциальных брешах в безопасности, которые могут возникнуть при использовании системных вызовов в PHP-скриптах (и о некоторых методах защиты). Эта мини-серия статей посвящёна подводным камням при написании защищённых PHP-приложений; однако же, в последней моей статье я не буду рассказывать вам о каких-либо новых дырах в безопасности. Я считаю, что лучшим завершением нашего разговора о безопасности будут рассказ о средствах обработки и протоколирования ошибок, и краткая ретроспектива всего изложенного в этой мини-серии.

Безопасность и ведение логов

Злоумышленнику, чтобы использовать ваш код в своих целях, нужно сначала определить его слабые места. Это достигается зондированием вашего приложения и сбором как можно большего количества информации о нём. Самый распространённый и эффективный способ - это попытки вызвать ошибки приложения.. Предположим, например, что злоумышленник ввёл некорректные данные в поле login-формы и спровоцировал следующее стандартное сообщение об ошибке в PHP:

Notice: Undefined index: content in /usr/local/apache/htdocs/index.php on line 22

Что нам может поведать это сообщение? Очевидно, то, что в коде есть массив с индексом content, неопределённый в строке 22 файла index.php. Благодаря этому сообщению, у злоумышленника есть подсказка о потенциальной дыре, через которую можно замусорить данные приложения. Может быть, это неопределённый индекс где-нибудь в $_GET или в $_POST. В принципе, смоделировать разные контексты и определив, как меняется при этом номер строки, где возникает ошибка, вы можете даже воссоздать общую идею работы скрипта. Кроме того, вид сообщения может также предоставить более подробную информацию о расположении файлов, с которыми работает скрипт (если мы работаем с функциями файловой системы), формат запросов (если работаем с базами данных), и прочие данные. Начнём с того, что обычно пользователям просто не нужно получать сообщение об ошибке со всеми подробностями, если что-то пошло не так. Ну а при определённых обстоятельствах такие подробности могут стать дырой в системе безопасности.

Первый шаг в решении этой проблемы - всевозможные меры по сокращению появления ошибок в скриптах. В каждом уважающем себя PHP-приложении все переменные должны быть определены, или, по крайней мере, проверены isset()-ом, если речь идёт о суперглобальных переменных. Однако сколько бы не было продумано и предусмотрено в вашем приложении, предполагать, что вы учли любые обстоятельства, нереалистично. Поэтому, в "охраняемых" приложениях должны быть реализованы системы обработки и регистрации ошибок.

Механизм регистрации ошибок в PHP

Смысл систем обработки и регистрации ошибок, по крайней мере, применительно к вопросам безопасности, - это отказывать злоумышленникам в доступе к информации о вашем приложении, но при этом предоставлять её разработчику. Правильная система регистрации ошибок запишет попытки скомпрометировать систему защиты вашего приложения и предоставит вам сведения о том, как и где надо усилить меры защиты.

Система регистрации ошибок в PHP может быть настолько простой или сложной, насколько вы пожелаете. В самом PHP есть выбор из нескольких встроенных механизмов обработки и регистрации ошибок. Например, PHP по умолчанию все ошибки выполнения скрипта выдаёт в браузер, но может быть сконфигурирован так, чтобы ошибки регистрировались, но не отображались. Это поведение контролируется из файла настройки php.ini директивами log_errors и display_errors. Включайте и выключайте сообщения об ошибках в зависимости от ваших программерских нужд. Обычная практика - это отображение ошибок без их регистрации на стадии разработки и отладки приложения. Для готового продукта всё наоборот: регистрация без отображения.

Как сообщения об ошибках PHP вываливаются в браузер, вы видели, а где PHP ведёт лог, если регистрация ошибок включена? Поведение механизма регистрации ошибок в PHP контролируется другой директивой, error_log. Значение может быть выставлено на имя файла, или на строку "syslog", или вовсе не выставлено (по умолчанию). В последнем случае, то есть когда значение error_log в php.ini не выставлено, то логи PHP ведутся за счёт средств web-сервера (например, Apache-вский error-log). Если значение выставлено на имя файла, то PHP будет писать лог в указанный файл, пока будут позволять системные разрешения. Если значение выставлено на ключевое слово syslog, то лог PHP будут вестись средствами журналирования операционной системы. Для UNIX-систем это стандартный системный журнал [syslog], для систем Windows NT и XP это журнал событий [event log].

PHP сам позаботится о регистрации ошибок, если вы используете встроенный обработчик ошибок, однако для своего обработчика (о нём речь пойдёт далее в статье) вам придётся всё сделать своими руками. Для подобных деяний PHP предлагает функцию error_log() со следующим синтаксисом:

error_log($message [, $message_type [, $dest [, $extra_info]]]);

В зависимости от выставленного значения необязательного параметра $message_type (ноль по умолчанию), произойдёт одно из нижеперечисленного:

  • если $message_type равен нулю, то сообщение об ошибке $message будет записано в лог, указанный в директиве конфигурации error_log.
  • если $message-type равен 1, то сообщение об ошибке $message будет отправлено по электронной почте на адрес, указанный в параметре $dest. Любые дополнительные почтовые заголовки можно указать в параметре $extra_info.
  • если $message_type равен 3, то сообщение об ошибке $message будет записано в файл, указанный в параметре $dest.

Примечание: Вы наверняка заметили, что для значения $message_type равного 2 поведение не предусмотрено. Это рудимент от PHP версии 3, где удалённая отладка поставлялась в стандартном релизе как его часть. Для PHP4 это уже не так. Функцию error_log() можно использовать для ведения логов в любой части приложения, но, как правило, она применяется как часть своего обработчика ошибок. Примеры - в мануале.

Классификация ошибок в PHP

Понимание классификации ошибок PHP, почти также важнО как правильная настройка системы регистрации ошибок. Данная модель определяет, какие типы ошибок регистрируются, как и когда происходит регистрация. Чтобы осмыслить саму модель классификации, вам нужно знать, какие типы ошибок вы можете получить в PHP, и каково значение каждой из них. Эти знания вы можете почерпнуть из мануала или из представленного ниже списка:

  • E_ERROR означает, что в самом PHP или в одном из его расширений возникла серьёзная внутренняя проблема (например, невозможность выделить память).
  • E_WARNING обычно выдаётся с целью привлечь внимание к потенциальной ошибке в коде, из-за которой он возможно будет работать не так, как задумано. Пример: какой-либо встроенной функции передаётся скалярное значение вместо ожидаемого составного, например массива.
  • E_NOTICE - это наименее значительная встроенная ошибка PHP. Этот тип почти никогда не означает, что приложение работает неправильно. Сообщения об ошибках этого типа призвано предупредить разработчика о таких вещах, как использование переменных до инициализации.
  • E_CORE_ERROR по фатальности равно E_ERROR, но выдаётся только при запуске ядра PHP.
  • E_CORE_WARNING - не фатальный коллега E_CORE_ERROR и схож с E_WARNING. выдаётся только при запуске ядра PHP.
  • E_COMPILE_ERROR означает серьёзную ошибку во время компиляции движком Zend Scripting Engine.
  • E_COMPILE_WARNING предупреждение, нефатально, как и E_WARNING, генерируется во время компиляции движком Zend Scripting Engine.
  • E_USER_ERROR зарезервирован исключительно для использования с функцией trigger_error() (см. далее в статье). По умолчанию обработка та же, что и у E_ERROR: сообщение об ошибке и прекращение исполнения.
  • E_USER_WARNING зарезервирован исключительно для использования с функцией trigger_error().По умолчанию обработка та же , что и у E_WARNING.
  • E_USER_NOTICE зарезервирован исключительно для использования с функцией trigger_error(). В отличие от E_NOTICE, который игнорируется по умолчанию, PHP выдаст сообщение о E_USER_NOTICE пользователю.

Устанавливаем режим вывода сообщений об ошибках

Директива конфигурации error_reporting определяет, какие сообщения об ошибках пишутся в лог или выбрасываются в браузер, когда оные ошибки случаются. Эта директива представляет собой "битовое поле", а это означает, что типы сообщений можно как угодно комбинировать с помощью логических операторов AND, OR, и NOT.В файле php.ini символ амперсанда (&) означает AND, символ конвейеризации (|) означает OR, а тильда (~) означает NOT. Таким образом, чтобы отображались или писались в лог только серьёзные ошибки, ваша директива примет следующий вид:

error_reporting = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR

Помимо стандартных типов ошибок директива error_reporting принимает специальный тип, E_ALL, представляющий все возможные типы ошибок сразу, то есть как будто вы OR-ами соединила все стандартные типы. Приведённая ниже директива заставляет отображать или писать в лог все типы ошибок, кроме ошибок из коллекции E_USER:

error_reporting = E_ALL & ~E_USER_ERROR & ~E_USER_WARNING & ~E_USER_NOTICE

По умолчанию директива error_reporting сообщает в браузер или лог обо всех ошибках, кроме E_NOTICE.

Вне зависимости от конфигурации PHP в части отлова ошибок, есть несколько способов контроля ситуации во время исполнения скрипта. Самый простой способ - использовать оператор подавления сообщений, @. Поставьте @ перед каким-либо выражением и PHP втихомолку проигнорирует ошибку, если таковая случится при вычислении выражения. Например:

<?php
echo @$myvar;
?>

не выкинет сообщение класса E_NOTICE, даже несмотря на то, что $myvar неопределена. Ошибку эту PHP проигнорирует и не напишет ничего.

Другой способ изменить настройку управления ошибками в PHP - это использовать функцию error_reporting(). Данная функция непосредственно изменяет "внутреннее" значение конфигурационной директивы error_reporting. Синтаксис функции таков:

error_reporting([$error_value])

где факультативный параметр $error_value - это новое значение битового поля. В PHP определены константы для всех приведённых выше классов ошибок, и из них можно составить комбинацию с помощью логических операторов PHP. Результатом работы следующей инструкции станет то, что PHP будет сообщать только о серьёзных ошибках, как и в одном из предыдущих примеров:

<?php
error_reporting
(E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
?>

Независимо от того, какое значение будет передано в качестве параметра в error_reporting(), на выходе мы всегда будем иметь целое число, равное ранее установленному значению директивы error_reporting. Такое поведение позволяет вам применить особый режим вывода сообщений об ошибках для небольшой части вашего приложения и с лёгкостью восстановить предыдущий режим:

<?php
$old_error
= error_reporting(0); /* Сообщения об ошибках больше не генерируем */
/* тут какой-то код */
error_reporting($old_error); /* восстанавливаем предыдущий режим вывода сообщений об ошибках. */
?>

Обработчики ошибок, определяемые программистом

Помимо встроенных средств обработки ошибок PHP также позволяет вам написать ваш собственный обработчик. Определённый вами лично обработчик ошибок позволяет вам осуществлять полный контроль над тем, как ваше web-приложение будет реагировать на ошибки, сгенерированы ли они PHP или сброшенные функцией trigger_error(). Чтобы задействовать определённую вами обработку ошибок, необходимо определить следующим способом:

<?php
function my_handler($error_code, $error_msg [, $error_file [,
$error_line [, $vars]]]) {
    
/* Собственно реализация обработчика */
}
?>

Как описано в нашем примере, обработчик ошибок должен принимать параметры $error_code и $error_msg; первый - это класс ошибки (например, E_ERROR), а второй - это сообщение, привязанное к данному классу. Ваш обработчик ошибок способен принимать до трёх дополнительных параметров: $error_file, $error_line, и $vars. Первые два - это PHP-файл и строка в нём, где ошибка имела место. Последний параметр, $vars, представляет собой ассоциативный массив, в котором содержатся имя и значение каждой переменной, которые были в области видимости на момент возникновения ошибки.

Создав такую функцию, её надо зарегистрировать в PHP как действующий обработчик ошибок. Для этого используйте функцию set_error_handler(), синтаксис её таков:

set_error_handler($func_name)

где $func_name - это строка с именем определённой вами функции (то есть, для моего примера, my_handler). Эта функция вернёт название предыдущего обработчика, то есть при желании можно это название сохранить и потом вернуть всё на место. При использовании определённого вами обработчика ошибок, держите в голове следующие моменты:

  • Действие вашего обработчика не распространяется на "фатальные" ошибки. Ваш обработчик будет вызываться только для ошибок класса E_WARNING, E_NOTICE и коллекции E_USER. Для всех прочих ошибок будет вызываться встроенный обработчик, как если бы вы вовсе не определяли свой альтернативный обработчик.
  • Если задействован обработчик, определённый программистом, PHP будет вызывать его при возникновении любой из вышеперечисленных ошибок, конфигурационная директива error_reporting игнорируется. Какая-либо реакция на error_reporting() определяется также в обработчике.
  • Если только исполнение скрипта не прерывается в обработчике с помощью die() или exit(), скрипт продолжит работу, невзирая на класс ошибки. Прекращение работы скрипта при необходимости определяется в обработчике.

Последняя функция на сегодня, trigger_error(). Эта функция используется для генерации ошибки, определённой разработчиком. Синтаксис функции таков:

trigger_error($msg [, $error_code])

где $msg - это строка с сообщением и описанием об ошибке, а факультативный параметр $error_code - класс ошибки из коллекции E_USER. Если второй параметр опускается, PHP автоматически применяет E_USER_NOTICE. Функция trigger_error() предназначена для использования в связке с обработчиком, определённым программистом; однако если такой обработчик не был определён, PHP среагирует на ошибку через стандартный обработчик.

В качестве резюме

В завершение этой мини-серии, посвящённой некоторым аспектам безопасности в PHP, я хотел бы ещё раз обозначить тезисы трёх статей. При написании web-приложения на PHP (да и любых других приложений на любом же языке), самое большее, что вы можете сделать для повышения уровня безопасности в вашем приложении - это держать в голове все возможные пути компрометации оной безопасности. Используются ли системные вызовы? Что делается для их защиты от непредусмотренного использования? Как приложение будет реагировать на некорректные данные от пользователя? Какие меры предусмотрены для фильтрации данных, полученных от пользователя? При разработке приложения вы должны всегда задавать себе все эти вопросы.

В конечном счёте, любой текст (включая тот, что у вас перед глазами) может только лишь чему-то научить. Вы получили некоторые начальные знания о ведении логов и проверке данных на корректность, вам же и решать, будете ли вы применять эти знания в разработке ваших приложений. Тщательность и внимательное отношение к каждой детали - лучшие инструменты разработчика при создании системы защиты для приложения. Конечно, злоумышленники используют стандартные средства, чтобы заставить ваше приложение повести себя непредвиденным образом. Однако сама природа злого умысла говорит о том, что всегда найдётся то, чего вы не предусмотрели. А раз найдено, значит использовано.

В моей следующей статье я немного сбавлю обороты и от безопасности перейду к обзору различных инструментов для работы с данными и управления ими. Итак, до встречи, счастливо покодить!

Джон Коггсхол (John Coggeshall): специалист и один из создателей PHP. Недосыпания по причине PHP начались около пяти лет назад.

 
 » Обсудить эту статью на форуме

 
 Сборник статей 
 Содержание раздела 
Есть еще вопросы или что-то непонятно - добро пожаловать на наш  форум портала PHP.SU 
 

 
Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS