PHP libxml_set_external_entity_loader()函数

libxml_set_external_entity_loader() 函数用于设置自定义的外部实体加载器,用于处理XML文档中的外部实体引用。这在增强XML处理的安全性和灵活性方面非常有用。

函数语法

libxml_set_external_entity_loader(?callable $resolver = null): bool

参数说明

参数 类型 描述
resolver callable|null 回调函数,用于加载外部实体。传入null可以恢复默认加载器

返回值

成功时返回 true,失败时返回 false

使用示例

示例1:自定义外部实体加载器

创建一个自定义的外部实体加载器,用于控制外部实体的访问:

<?php
// 自定义外部实体加载器
function customEntityLoader($public, $system, $context) {
    // 只允许从特定域名加载外部实体
    $allowedDomains = ['https://trusted-domain.com/', 'https://api.example.com/'];

    foreach ($allowedDomains as $domain) {
        if (strpos($system, $domain) === 0) {
            return file_get_contents($system);
        }
    }

    // 对于不允许的域,返回空字符串
    return '';
}

// 设置自定义加载器
libxml_set_external_entity_loader('customEntityLoader');

// 禁用实体加载(安全选项)
libxml_disable_entity_loader(false);

// 解析XML
$xml = '<?xml version="1.0"?>
<!DOCTYPE root [
    <!ENTITY external SYSTEM "https://trusted-domain.com/data.xml">
]>
<root>
    <content>&external;</content>
</root>';

$dom = new DOMDocument();
$dom->loadXML($xml);
echo $dom->saveXML();

// 恢复默认加载器
libxml_set_external_entity_loader(null);
?>

示例2:防止XXE攻击

使用libxml_set_external_entity_loader()来防止XML外部实体(XXE)攻击:

<?php
// 安全的外部实体加载器 - 阻止所有外部实体
function secureEntityLoader($public, $system, $context) {
    // 记录尝试访问的外部实体(用于监控)
    error_log("Attempt to load external entity: " . $system);

    // 抛出异常,阻止外部实体加载
    throw new Exception("External entity loading is not allowed: " . $system);

    // 或者返回空字符串/固定内容
    // return '';
}

// 设置安全加载器
libxml_set_external_entity_loader('secureEntityLoader');

try {
    // 尝试解析包含外部实体的XML
    $xml = '<?xml version="1.0"?>
    <!DOCTYPE root [
        <!ENTITY xxe SYSTEM "file:///etc/passwd">
    ]>
    <root>&xxe;</root>';

    $dom = new DOMDocument();
    $dom->loadXML($xml);
    echo "XML parsed successfully (this should not happen)";
} catch (Exception $e) {
    echo "Security error: " . $e->getMessage();
}

// 恢复默认加载器
libxml_set_external_entity_loader(null);
?>

示例3:从数据库加载实体

从数据库加载预定义的外部实体内容:

<?php
// 假设这是数据库连接(示例用数组模拟)
$entityDatabase = [
    'user_data' => '<user><name>John Doe</name><email>john@example.com</email></user>',
    'app_config' => '<config><version>1.0</version><debug>false</debug></config>'
];

function databaseEntityLoader($public, $system, $context) {
    global $entityDatabase;

    // 从system标识符中提取实体名称
    $entityName = basename($system, '.xml');

    if (isset($entityDatabase[$entityName])) {
        return $entityDatabase[$entityName];
    }

    // 如果没有找到,返回默认内容或空字符串
    return "<error>Entity not found: $entityName</error>";
}

// 设置数据库加载器
libxml_set_external_entity_loader('databaseEntityLoader');

// 解析使用数据库实体的XML
$xml = '<?xml version="1.0"?>
<!DOCTYPE root [
    <!ENTITY user SYSTEM "user_data.xml">
    <!ENTITY config SYSTEM "app_config.xml">
]>
<root>
    &user;
    &config;
</root>';

$dom = new DOMDocument();
$dom->loadXML($xml);
echo htmlspecialchars($dom->saveXML());

// 恢复默认加载器
libxml_set_external_entity_loader(null);
?>

注意事项

  • 在PHP 8.0及以上版本,建议同时使用libxml_disable_entity_loader(false)来启用实体加载
  • 自定义加载器应该正确处理错误情况,避免泄露敏感信息
  • 在生产环境中,应该限制外部实体的来源,防止XXE攻击
  • 记得在不需要自定义加载器时恢复默认加载器

兼容性

PHP版本 支持情况
PHP 5.3+ 支持
PHP 7.0+ 支持
PHP 8.0+ 支持