应同时进行路径归一化校验和XML解析器安全配置:先用os.path.realpath()或Paths.get().toRealPath()解析用户路径并与白名单根目录比对前缀,再禁用DOCTYPE声明及外部实体加载。

XML解析时如何阻止../路径遍历
XML文件上传后若直接用用户输入的文件名拼接本地路径(比如读取document.dtd或外部实体),攻击者可通过../向上跳转目录,读取/etc/passwd或应用配置文件。关键不是“禁用外部实体”,而是从路径构造环节就切断遍历可能。
- 所有用户可控的文件名/路径参数必须经过
os.path.realpath()(Python)或Paths.get().toRealPath()(Java NIO)解析后,再与白名单根目录比对前缀 - 禁止用
String.replace("../", "")或正则简单过滤——....//、%2e%2e%2f、..\(Windows)仍可绕过 - 若使用
DocumentBuilder或libxml2加载XML,必须显式关闭外部实体:设置setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
Java中SAX/DOM解析器的安全配置
默认SAXParserFactory和DocumentBuilderFactory允许外部DTD和实体,这是XXE和路径遍历的共同入口。仅靠应用层校验路径不解决根本问题。
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
-
disallow-doctype-decl直接禁止/code>,最彻底 - 若业务必须支持DOCTYPE(极少见),则需配合
EntityResolver拦截所有resolveEntity调用,返回空InputSource - 注意:Tomcat 8.5+ 默认已禁用外部实体,但Spring Boot内嵌Tomcat若覆盖了
DocumentBuilderFactorybean,仍可能回退到不安全状态
Python lxml与defusedxml的选型差异
lxml默认开启外部实体,且etree.parse()会自动加载file://协议资源;而defusedxml是专为防御XXE设计的封装层,但不处理路径拼接逻辑。
- 用
defusedxml.lxml.fromstring()替代原生etree.fromstring(),它会禁用load_dtd=True和resolve_entities=True - 但
defusedxml不管parse("user_input.xml")里的文件路径——你仍需确保user_input.xml是经os.path.realpath()校验后的绝对路径 - 避免混用:
lxml.etree.parse()+ 手动设parser=XMLParser(resolve_entities=False)易遗漏,不如直接切到defusedxml
上传文件名的白名单校验不能只看扩展名
攻击者常传payload.xml?x=1或shell.xmll绕过.xml后缀检查,更危险的是用../../config.yaml作文件名,诱导服务端解析非XML文件引发二次漏洞。
- 提取原始文件名后,先用
os.path.basename()剥离路径,再用re.match(r'^[a-zA-Z0-9_-]+\.xml$', name)校验格式 - 拒绝任何含
.、/、\、%、的文件名——不是过滤,是直接拒收 - 存储时强制重命名:
uuid4().hex + ".xml",彻底断开用户输入与文件系统路径的映射
../就可能穿透进来。










