Все статьи раздела «Программирование»

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