防止XXE攻击:XML解析存在的安全问题指引

描述

XML 解析器无法预防和限制外部实体进行解析,这会使解析器暴露在 XML External Entities 攻击之下。
XML组件默认没有禁用外部实体引用,XML 实体可动态包含来自给定资源的数据。当使用用户的输入作为文档的一部分进行动态构建XML时,XML 解析器可以访问由用户输入的 URI 所指定的资源,因此可能导致读取任意文件;执行系统命令;探测内网端口;攻击内网网站等危害。

案例图解


外界攻击者通过模拟回调通知,在回调通知中引入不安全的XML,商户服务器上执行系统命令。

修复方法

XXE漏洞需要您在回调处理代码里面解析XML之前,加入禁用实体解析的代码,不同语言设置的内容不同,下面提供了几种主流开发语言的设置指引(您可以根据关键字找到xml解析组件采取对应方法升级):

【JAVA】

1. 读取
  • DocumentBuilderFactory
/**
 * 注意:
 * - setFeature() 需要在 newDocumentBuilder() 之前才能够生效
 * - 当 setFeature 报错时,记得设置 System.setProperty
 **/

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; // catching unsupported features

// 当出现 javax.xml.parsers.DocumentBuilderFactory.setFeature(Ljava/lang/String;Z)V 错误时,
// 需要加入以下代码,报错原因:https://blog.csdn.net/u011479200/article/details/78044824
System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
String FEATURE = "";
try {

    // 方式一:完全禁止DTD,如果传入文档包含DOCTYPE声明,则抛出错误。
    FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
    dbf.setFeature(FEATURE, true);

    
    // 方式二:如果出于某种原因需要支持内联DOCTYPE,那么确保以下实体设置被禁用(需要JDK7+),
    //        并注意防范[SSRF攻击](http://cwe.mitre.org/data/definitions/918.html)
    //        和拒绝服务攻击(比如十亿次`lol`或通过`jar:`进行解压炸弹)。
    FEATURE = "http://xml.org/sax/features/external-general-entities";
    dbf.setFeature(FEATURE, false); // 不包括外部一般实体。
    FEATURE = "http://xml.org/sax/features/external-parameter-entities";
    dbf.setFeature(FEATURE, false); // 不包含外部参数实体或外部DTD子集。
    FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
    dbf.setFeature(FEATURE, false); // 忽略外部DTD
    dbf.setXIncludeAware(false);
    dbf.setExpandEntityReferences(false);

} catch (ParserConfigurationException e) {
    logger.info("ParserConfigurationException was thrown. The feature '" +
    FEATURE + "' is probably not supported by your XML processor."); // setFeature失败
} catch (SAXException e) {
    logger.warning("DOCTYPE被传递到XML文档中"); // 在Apache上,当禁用DOCTYPE时会抛出这个错误
} catch (IOException e) {
    logger.error("IOException occurred, XXE may still possible: " + e.getMessage()); // 指向一个不存在的文件
}

DocumentBuilder safebuilder = dbf.newDocumentBuilder();
  • SAXReader
SAXReader sax = new SAXReader();
// 需加入下面这句进行防护
sax.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Document doc = sax.read(XMLFILE);
Element root = doc.getRootElement();
List list = root.elements();

2. 输出/转换
  • 要保护 TransformerFactory,应设置下列功能
TransformerFactory transFact =TransformerFactory.newInstance();

transFact.setFeature(XMLConstants.ACCESS_EXTERNAL_DTD,false);
// 如果使用的是java 8,上面一句需要改为:
// transFact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// 原因参考:https://stackoverflow.com/questions/27128578/set-feature-accessexternaldtd-in-transformerfactory

Transformer trans =transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);
  • 或者使用安全配置的 XMLReader 来设置转换源
XMLReader reader =XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/external-general-entities",false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities",false);
Source xmlSource = new SAXSource(reader,new InputSource(new FileInputStream(xmlFile)));
Source xsltSource = new SAXSource(reader,new InputSource(new FileInputStream(xsltFile)));
Result result = newStreamResult(System.out);
TransformerFactory transFact =TransformerFactory.newInstance();
Transformer trans =transFact.newTransformer(xsltSource);
trans.transform(xmlSource, result);

3. XPath查询防护
// 若需要使用XPath进行查询,请使用ESAPI对用户输入进行转义。
ESAPI.encoder().encodeForXPath(xpath);

参考链接:XML实体注入风险


【PHP】 解析XML代码前加入:

libxml_disable_entity_loader(true); //关键代码
$xml = simplexml_load_string($xmlContent);

【.Net】

XmlDocument doc= new XmlDocument();
doc.XmlResolver = null;//关键代码

【ASP】

Set xmldom = Server.CreateObject("MSXML2.DOMDocument")
xmldom.resolveExternals = false  '关键代码

【Python】

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

【c/c++(常用库为libxml2 libxerces-c)】 【libxml2】
确保关闭配置选项:XML_PARSE_NOENT 和 XML_PARSE_DTDLOAD
2.9版本以上已修复XXE


【Coldfusion/lucee和node.js】
请更新到库的最新版本


【libxerces-c】:
如果用的是XercesDOMParser:

XercesDOMParser *parser = new XercesDOMParser;
parser->setCreateEntityReferenceNodes(false);

如果是用SAXParser:

SAXParser* parser = new SAXParser;
parser->setDisableDefaultEntityResolution(true);

如果是用SAX2XMLReader:

SAX2XMLReader* reader = XMLReaderFactory::createXMLReader();
parser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);

参考文档:【微信支付XML安全实践】

你可能感兴趣的:(防止XXE攻击:XML解析存在的安全问题指引)