☰
CodeQL C++ 查询实战:`cpp/external-entity-expansion`(XML 外部实体 XXE 检测)及其 0.1.2 版库覆盖扩展
2026/9/29 5:42:35 网站建设 项目流程
  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

本篇以 CodeQL C++ 查询包 0.1.2 版变更说明(cpp/ql/src/change-notes/released/0.1.2.md)为核心线索,深入讲解 C++ 语言包中的 XXE 检测查询cpp/external-entity-expansion:其漏洞模型、基于流状态(flow state)的数据流分析实现、0.1.2 中扩展支持的 XML 库与解析接口,以及如何参照仓库内的 QL 源码、样例与测试用例来验证告警行为。读完本文,你将能够读懂该查询的建模思路,并掌握修复 XXE 漏洞的安全配置写法与回归测试方法。

变更背景:从新增到扩展

0.1.2 版的变更说明内容非常聚焦,属于"Minor Analysis Improvements":

The "XML external entity expansion" (cpp/external-entity-expansion) query has been extended to support a broader selection of XML libraries and interfaces.

而回溯 0.1.1 版(cpp/ql/src/change-notes/released/0.1.1.md)可以看到该查询正是在上一版才被引入:

An new querycpp/external-entity-expansionhas been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks.

也就是说,0.1.1 完成查询的"从无到有",0.1.2 则完成了"从单点支持到更广泛 XML 库/接口覆盖"的扩展。结合当前仓库快照的源码(XXE.ql、XML.qll、Xerces.qll、Libxml2.qll)可见,扩展后的建模面覆盖了 Xerces-C++ 与 libxml2 两大 C/C++ XML 解析库及其多种解析入口。

查询元数据与告警形态

查询本体位于 cpp/ql/src/Security/CWE/CWE-611/XXE.ql,其头注释定义了完整的元数据:

元数据项值说明
@nameXML external entity expansion查询显示名
@kindpath-problem输出"源 → 汇"的完整数据流路径
@idcpp/external-entity-expansion稳定查询 ID,被 suite 与 SARIF 引用
@problem.severitywarning告警级别
@security-severity9.1安全严重度(对应 CVSS 风格评分区间)
@precisionhigh低误报承诺
@tagssecurity, external/cwe/cwe-611对应 CWE-611:对 XML 外部实体的不当限制

告警文案为:

This $@ is not configured to prevent an XML external entity (XXE) attack.

其中$@指向源节点(即 XML 解析器对象的创建/解析调用),并以 "XML parser" 作为链接标签。由于是path-problem,结果会以路径图形式呈现从解析器创建到解析调用的完整流向,便于开发者定位是哪个对象、在哪一步进入了不安全配置状态。

XXE 漏洞检测目标:查询要回答的问题

XXE.qhelp 对该查询的检测目标做了权威说明:用弱配置的 XML 解析器解析不可信 XML 文件,可能触发 XML 外部实体(XXE)攻击,攻击者利用外部实体引用可达:

  • 读取系统上的任意文件(信息泄露);
  • 实施拒绝服务(DoS);
  • 发起服务端请求伪造(SSRF)。

即使解析结果不会返回给用户,DoS 与带外(out-of-band)数据检索技术仍可能窃取敏感数据,因此该问题即便在"结果不外泄"的场景下依然成立。查询的@description同样强调了两类后果:机密数据泄露与拒绝服务(见 XXE.ql)。

实现原理:用"流状态"跟踪解析器配置

该查询的独到之处在于它跟踪的不是普通的污点数据,而是"XML 解析器对象的配置状态"。这一设计体现在 XML.qll 的抽象层中:

流状态类型TXxeFlowState

newtype TXxeFlowState定义了两种配置状态:

  • TXercesFlowState(int disabledDefaultEntityResolution, int createEntityReferenceNodes):两个布尔位分别记录AbstractDOMParser/SAXParser上setDisableDefaultEntityResolution与setCreateEntityReferenceNodes的取值;
  • TLibXml2FlowState():libxml2 库的状态(无内部维度,其安全性由解析函数传入的xmlParserOption决定)。

抽象库接口XmlLibrary

abstract class XmlLibrary extends string定义了两种谓词:

  • configurationSource:识别"可能不安全的配置对象"的创建点,并给出初始流状态;
  • configurationSink:识别"用该配置对象解析 XML"的调用点。

XercesFlowState(Xerces.qll)将TXercesFlowState包装成可读写的状态类,并通过encodeXercesFlowState谓词完成状态与两个布尔位的编解码。

状态转换与数据流配置

XxeConfig(见 XXE.ql)实现了DataFlow::StateConfigSig:

  • isSource/isSink分别委托给各XmlLibrary的configurationSource/configurationSink;
  • isAdditionalFlowStep让XxeFlowStateTransformer(如setDisableDefaultEntityResolution(...)调用)在数据流传播过程中改写流状态;
  • isBarrier在状态发生转换的调用节点阻断旧状态继续传播,从而精确区分"调用前"与"调用后"的配置。

最终由DataFlow::GlobalWithState<XxeConfig>构建全局带状态数据流(XXE.ql),flowPath(source, sink)产出完整路径。值得留意的是,XML.qll 注释明确要求转换谓词必须是幂等的(transform(transform(x)) = transform(x)),这是该实现方式下状态转换的必要约束。

0.1.2 扩展的 XML 库与接口覆盖

"Broader selection of XML libraries and interfaces" 的具体内容,可以从当前仓库的建模文件中还原:

Xerces-C++ 侧(Xerces.qll)

模型以AbstractDOMParser/SAXParser对象及其配置方法为核心。测试目录(cpp/ql/test/query-tests/Security/CWE/CWE-611)中对应的用例文件印证了覆盖面:

  • tests.cpp:XercesDOMParser及其setDisableDefaultEntityResolution、setCreateEntityReferenceNodes组合;
  • tests2.cpp:SAXParser变体;
  • tests3.cpp:createXMLReader(SAX2 XMLReader)接口;
  • tests5.cpp:createLSParser(DOM Level 3 LSParser)接口。

也就是说,0.1.2 的扩展不只是"多认一个类",而是把 Xerces 家族中 DOM(XercesDOMParser、createLSParser)与 SAX(SAXParser、createXMLReader)两条接口线都纳入了同一套状态建模。

libxml2 侧(Libxml2.qll)

Libxml2ParseCall类按函数名登记了解析入口及其xmlParserOption参数位置:

函数options 参数位置
xmlCtxtUseOptions1
xmlReadFile2
xmlCtxtReadFile、xmlParseInNodeContext、xmlReadDoc、xmlReadFd3
xmlCtxtReadDoc、xmlCtxtReadFd、xmlReadMemory4
xmlCtxtReadMemory、xmlReadIO5
xmlCtxtReadIO6

覆盖了文档读取(file/doc/memory/fd/IO)与上下文解析两大入口族。测试文件 tests4.cpp 展示了判别逻辑:传入XML_PARSE_NOENT、XML_PARSE_DTDLOAD或两者按位或组合时告警(BAD),而传入0或XML_PARSE_OPTION_HARMLESS时不告警(GOOD)。

修复建议:按库关闭外部实体处理

XXE.qhelp 给出的通用建议是:解析不可信数据时应禁用外部实体处理,具体做法取决于所用库。同时提醒:部分库(如较新版本的 libxml)默认即禁用实体展开,除非显式开启,否则无需额外操作——这也解释了为何xmlReadFile(..., 0)与XML_PARSE_OPTION_HARMLESS在测试中被判定为安全。

仓库内置的成对样例给出了最直观的修复对照:

不安全写法(XXEBad.cpp)——解析器以默认状态构造,setDisableDefaultEntityResolution为false,可能展开外部实体引用:

XercesDOMParser *parser = new XercesDOMParser(); parser->parse(data); // BAD (parser is not correctly configured, may expand external entity references)

安全写法(XXEGood.cpp)——显式关闭默认实体解析:

XercesDOMParser *parser = new XercesDOMParser(); parser->setDisableDefaultEntityResolution(true); parser->parse(data);

测试用例进一步揭示了 Xerces 配置的两个维度必须同时满足:仅设setDisableDefaultEntityResolution(true)而setCreateEntityReferenceNodes为false仍会告警(tests.cpp);两者均为true才判定安全(tests.cpp)。同一测试文件还覆盖了配置在运行中途被反复切换的场景(tests.cpp),验证流状态机对状态改写点的处理。

回归测试与预期结果:验证扩展是否生效

该查询配备了完整的 QL 测试套件,位于 cpp/ql/test/query-tests/Security/CWE/CWE-611,包含多组 C++ 测试输入与对应的 XXE.expected 期望输出。期望文件中的#select段列出所有路径结果,例如:

| tests.cpp:17:2:17:2 | *p | ... | call to XercesDOMParser | ... | This $@ is not configured to prevent an XML external entity (XXE) attack. | ... | XML parser | | tests4.cpp:26:34:26:48 | XML_PARSE_NOENT | ... | XML_PARSE_NOENT | ... | This $@ is not configured to prevent an XML external entity (XXE) attack. | ... | XML parser |

从期望输出可以看到告警同时覆盖了 Xerces(XercesDOMParser、SAXParser、createXMLReader、createLSParser)与 libxml2(XML_PARSE_NOENT、XML_PARSE_DTDLOAD及其组合、透传的flags变量),这正是 0.1.2"扩展支持更广泛 XML 库与接口"在测试层面的落点。开发者可参照该目录结构为自己的库扩展补充用例,用codeql test run运行后比对.expected文件即可验证建模行为。

关联文档与演进

  • 变更记录入口:cpp/ql/src/change-notes/released/0.1.2.md;
  • 新增记录(0.1.1):cpp/ql/src/change-notes/released/0.1.1.md;
  • 后续版本在 cpp/ql/src/CHANGELOG.md 中持续记录。

从当前仓库整体结构看,该查询与 CWE-611 条目(cpp/ql/src/Security/CWE/CWE-611)下的XML.qll抽象层、Xerces.qll/Libxml2.qll库模型、XXE.qhelp说明及XXEBad.cpp/XXEGood.cpp样例共同构成一个自洽的检测单元。若要为新的 XML 库(如 pugixml、tinyxml)扩展覆盖,推荐路径是:在XML.qll的XmlLibrary抽象类下新增子类实现configurationSource/configurationSink,定义对应的TXxeFlowState分支与幂等的XxeFlowStateTransformer,并在 cpp/ql/test/query-tests/Security/CWE/CWE-611 下补充测试与期望输出。

  • 静态分析
  • SAST
  • 应用安全
  • 漏洞扫描
  • 代码质量

【免费下载链接】codeql

CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security

项目地址:https://gitcode.com/gh_mirrors/co/codeql
点击查看免费下载

相关推荐

上一篇:高德地图API深度集成:DistrictSearch与DistrictExplorer使用详解
下一篇:FontForge 原生脚本语言内置函数全解析:从命令注册表到 300+ 内置过程的完整实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询