Разграничение доступа в 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'.