描述
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<Element> 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安全实践】