Xml set external entity ref handler: примеры (PHP)

Обработка внешних сущностей в XML парсере PHP
Раздел: XML
xml_set_external_entity_ref_handler(resource parser, callable handler): bool

Функция xml_set_external_entity_ref_handler

Функция xml_set_external_entity_ref_handler() устанавливает обработчик для внешних ссылок на сущности в XML-парсере. Она используется при разборе XML-документов, содержащих объявления внешних сущностей, таких как внешние DTD или включения текста из других файлов.

Аргументы функции
  • parser — обязательный аргумент, ссылка на XML-парсер, созданный функцией xml_parser_create().
  • handler — обязательный аргумент, имя функции обратного вызова, которая будет вызвана при обнаружении внешней ссылки. Обработчик должен принимать пять параметров: парсер, открытое имя сущности, базовый идентификатор, системный идентификатор и публичный идентификатор.

Функция возвращает логическое значение: true при успешной установке обработчика, false в случае ошибки.

Примеры использования

Пример 1: Базовый обработчик
<?php
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, "externalEntityHandler");

function externalEntityHandler($parser, $openEntityNames, $base, $systemId, $publicId) {
    echo "Внешняя сущность: $systemId\n";
    return true;
}

$xml = '<!DOCTYPE doc [<!ENTITY ext SYSTEM \"external.txt\">]>';
$xml .= '<doc>&ext;</doc>';
xml_parse($parser, $xml);
?>
Внешняя сущность: external.txt
Пример 2: Отказ от обработки
<?php
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, "ignoreExternalEntities");

function ignoreExternalEntities($parser, $openEntityNames, $base, $systemId, $publicId) {
    return false;
}

$xml = '<!DOCTYPE doc [<!ENTITY ext SYSTEM \"http://example.com/file.xml\">]>';
$xml .= '<doc>&ext;</doc>';
if (!xml_parse($parser, $xml)) {
    echo "Ошибка парсинга: " . xml_error_string(xml_get_error_code($parser));
}
?>
Ошибка парсинга: External entity handling not set

Альтернативные функции в PHP

Для обработки XML в PHP существуют другие функции и расширения:

  • xml_set_unparsed_entity_decl_handler() — устанавливает обработчик для неразбираемых объявлений сущностей. Отличается типом обрабатываемых сущностей.
  • DOMDocument::loadXML() с настройкой LIBXML_NOENT — позволяет автоматически разрешать сущности, но требует осторожности из-за рисков безопасности.
  • SimpleXML — не поддерживает установку пользовательских обработчиков для внешних сущностей, но проще в использовании для базовых задач.

Функцию xml_set_external_entity_ref_handler() предпочтительно использовать при необходимости тонкого контроля над обработкой внешних ресурсов, особенно в контексте безопасности.

Аналоги в других языках

Xml set external entity ref handler в Python

В модуле xml.sax можно установить обработчик через setEntityResolver.

import xml.sax

class MyResolver(xml.sax.handler.EntityResolver):
    def resolveEntity(self, publicId, systemId):
        print(f"Запрос сущности: {systemId}")
        return None  # Отказ от загрузки

parser = xml.sax.make_parser()
parser.setEntityResolver(MyResolver())
parser.parse("document.xml")
JavaScript (Node.js)

В библиотеке sax-js можно использовать событие oncdata и отключать внешние сущности.

const sax = require('sax');
const parser = sax.parser(false, { xmlns: true });
parser.onopentag = function(node) { console.log(node.name); };
// Внешние сущности обычно отключены по умолчанию для безопасности.
parser.write('<doc>&ext;</doc>');

Xml set external entity ref handler в MySQL

MySQL не имеет прямой аналогии, так как не является языком для разбора XML. Однако в SQL-запросах можно использовать функцию ExtractValue(), но она не обрабатывает внешние сущности.

Типичные ошибки

Ошибка 1: Отсутствие возвращаемого значения
<?php
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, function($parser, $openEntityNames, $base, $systemId, $publicId) {
    // Нет возвращаемого значения
});
// Парсинг может завершиться ошибкой
?>
Предупреждение: обработчик должен возвращать логическое значение.
Ошибка 2: Неправильное количество аргументов
<?php
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, function($parser) {
    // Только один аргумент вместо пяти
    return true;
});
?>
Функция обратного вызова будет вызвана с некорректными данными, что приведет к непредсказуемому поведению.
Ошибка 3: Игнорирование безопасности
<?php
function unsafeHandler($parser, $openEntityNames, $base, $systemId, $publicId) {
    // Чтение файла без проверки
    $content = file_get_contents($systemId);
    xml_parse($parser, $content);
    return true;
}
?>

Такой код уязвим для XXE-атак, если система обрабатывает непроверенные XML-документы.

Изменения в версиях PHP

В PHP 8.0 функция xml_set_external_entity_ref_handler() не претерпела значительных изменений. Однако, начиная с PHP 8.0, расширение XML по умолчанию входит в ядро и всегда доступно. Ранее в некоторых сборках его нужно было включать отдельно.

В PHP 8.1 и 8.2 не было зафиксировано изменений в поведении этой функции. Важно отметить, что современные практики безопасности рекомендуют отключать обработку внешних сущностей по умолчанию, что можно сделать через libxml_set_external_entity_loader(NULL) в контексте DOM и SimpleXML.

Расширенные примеры

Пример 1: Кеширование внешних сущностей
Пример php
<?php
$cache = [];
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, function($parser, $openEntityNames, $base, $systemId, $publicId) use (&$cache) {
    if (isset($cache[$systemId])) {
        xml_parse($parser, $cache[$systemId]);
        return true;
    }
    if (filter_var($systemId, FILTER_VALIDATE_URL)) {
        $content = file_get_contents($systemId);
        $cache[$systemId] = $content;
        xml_parse($parser, $content);
        return true;
    }
    return false;
});

$xml = '<!DOCTYPE root [<!ENTITY inc SYSTEM \"https://example.com/data.xml\">]>';
$xml .= '<root>&inc;</root>';
xml_parse($parser, $xml);
?>

Обработчик кеширует содержимое внешних сущностей, избегая повторных загрузок.

Пример 2: Валидация источников
Пример php
<?php
$parser = xml_parser_create();
xml_set_external_entity_ref_handler($parser, function($parser, $openEntityNames, $base, $systemId, $publicId) {
    $allowedDomains = ['https://trusted.com/', 'file:///safe/path/'];
    $allowed = false;
    foreach ($allowedDomains as $domain) {
        if (strpos($systemId, $domain) === 0) {
            $allowed = true;
            break;
        }
    }
    if (!$allowed) {
        return false;
    }
    $content = file_get_contents($systemId);
    xml_parse($parser, $content);
    return true;
});
?>

Обработчик разрешает загрузку только из доверенных источников, что повышает безопасность.

Пример 3: Логирование запросов
Пример php
<?php
$parser = xml_parser_create();
$log = [];
xml_set_external_entity_ref_handler($parser, function($parser, $openEntityNames, $base, $systemId, $publicId) use (&$log) {
    $log[] = [
        'timestamp' => time(),
        'entity' => $openEntityNames,
        'system' => $systemId,
        'public' => $publicId
    ];
    // Отказ от загрузки, только логирование
    return false;
});

$xml = '<!DOCTYPE test [<!ENTITY secret SYSTEM \"file:///etc/passwd\">]><test/>';
xml_parse($parser, $xml);
print_r($log);
?>

Пример фиксирует попытки обращения к внешним сущностям без их фактической загрузки.

PHP xml_set_external_entity_ref_handler function comments

En
Xml set external entity ref handler Set up external entity reference handler