Разграничение доступа в PHP-скриптах
Способ ограничения прав на вызов функции в PHP-скрипте на основании анализа стека вызовов. Может быть использован, например, для защиты ядра многопользовательской CMS, где несколько web-мастеров могут использовать собственные PHP-скрипты.
Предупреждение: все, что написано в этой статье, имеет смысл для программистов, использующих PHP 4 версии от 4.3.0. Для тех, кто пишет под PHP 5 приемы, описанные здесь, смысла не имеют, пользуйтесь классами и разграничением доступа в них!
Описание проблемы
При разработке многопользовательской системы, например, публичной CMS, когда администраторам сайтов разрешено загружать на сервер PHP-скрипты, возникает проблема создания многоуровневой защиты различной системной информации. Например, CMS использует базу данных для хранения имен, паролей и прав доступа администраторов и необходимо предусмотреть защиту от модификации этой базы скриптами, загруженными в систему извне.
Одно из возможных решений этой задачи и описано в данной статье.
Решение
Критическая информация (например, имя пользователя и пароль к БД с важной информацией) сосредотачивается в выделенной функции, вызов которой разрешен лишь из функций с заданными именами (список доверенных функций). Здесь используется тот факт, что PHP (по крайней мере до версии 4 включительно) не поддерживает перегрузки, переопределения или удаления функции, а следовательно, никто не сможет в своем скрипте создать функцию с именем из списка доверенных.
Для получения информации о вызывающей функции используется функция debug_backtrace()
. Она возвращает массив, каждый элемент которого содержит информацию о элементе стека вызовов в данном месте программы. Элемент с нулевым индексом соответствует функции, вызвавшей debug_backtrace
, с индексом "1" – функции, вызвавшей эту и так далее. Каждый элемент массива в свою очередь представляет собой ассоциативный массив, в котором нас интересует элемент с ключом "function". Он содержит имя функции, соответствующей данному уровню стека вызовов. Проверив значение этого элемента для нужного уровня стека вызовов, функция может определить, является ли ее вызов разрешенным.
Пример:
<?php function SecureFunc() { $backtrace = debug_backtrace(); // print_r($backtrace); $is_trusted = count($backtrace) > 1 && $backtrace[1]['function'] == 'trustedfunc'; return $is_trusted ? 'password' : 'ERROR!!!'; } function TrustedFunc() { $secure_data = SecureFunc(); print "TrustedFunc: DATA IS $secure_data\r\n"; } function IntruderFunc() { $secure_data = SecureFunc(); print "IntruderFunc: DATA IS $secure_data\r\n"; } TrustedFunc(); IntruderFunc(); ?>
Запуск интерпретатора PHP из командной строки для обработки данного файла выдаст:
$ php secure_test.php TrustedFunc: DATA IS password IntruderFunc: DATA IS ERROR!!!
Обратите внимание, что имя функции следует писать строчными буквами, т.к. PHP не различает регистр имен функций. Еще более правильным было бы использование регистро-независимого сравнения (например strcasecmp
).
Желающим подробнее изучить работу функции debug_backtrace
стоит выполнить приведенный файл, убрав комментарий в строке с print_r
. Эту функцию также можно использовать для вывода сообщений об ошибках, т.к. печать стека вызовов позволит лучше проследить потоки управления в программе.
Подробнее о функции debug_backtrace
Формат вызова:
$stack_list = debug_backtrace();
Переменная $stack_list
получает массив, каждый элемент которого соответствует одному из элементов стека вызовов для текущего контекста исполнения. Массив имеет числовые индексы, индекс 0 соответствует коду, вызвавшему функцию debug_backtrace
.
Каждый элемент массива представляет собой также массив из следующих элементов:
function
– имя исполняемой функции;file
– имя файла, из которго вызвана функция'function'
;line
– номер строки в файле'file'
, в которой вызвана функция'function'
;args
– массив с числовыми индексами (от нуля), содержащий значения аргументов, переданных при вызове функции'function'
.