xml本身不执行sql,但被当作可执行上下文解析时会引发xpath注入、报错注入和xml实体膨胀三重风险;防御核心是切断xml内容到sql执行的链路,而非仅校验格式。

直接回答:XML本身不执行SQL,但当它被当作“可执行上下文”解析(比如进UPDATEXML、EXTRACTVALUE或拼进动态SQL),就等于给攻击者开了XPath注入+报错注入+实体膨胀三重后门。防御核心不是“验XML格式”,而是切断“XML内容 → SQL执行”的链路。
别让XML节点值直通SQL函数的第二个参数
常见错误是把用户提交的XML当“普通字符串”存着,后续却用UPDATEXML或EXTRACTVALUE提取字段再拼SQL——哪怕用了#{xml}占位符,函数内部仍会解析XPath表达式,' OR 1=1这类内容照样能触发报错注入或布尔盲注。
- 错误写法:
SELECT * FROM users WHERE phone = UPDATEXML(?, '/root/phone/text()', '')—— ?是预编译参数,但函数体内XPath仍被动态执行 - 正确做法:用标准XML解析器(如Java的
DocumentBuilder、Python的xml.etree.ElementTree)先提取phone文本,再作为独立参数传入PreparedStatement或MyBatis #{} - MySQL 8.0+可用
XMLQUERY替代UPDATEXML,它默认不展开外部实体,且支持命名空间白名单控制
接收层就要拦截DOCTYPE和ENTITY声明
很多团队等解析器报错才处理,其实攻击已在内存里完成——sp_xml_preparedocument或JAXP解析器遇到恶意DTD时,可能直接触发XML Bomb(指数级实体膨胀),导致java.lang.OutOfMemoryError: Java heap space。
- 在HTTP请求入口或API网关层,用正则快速扫描:
/,匹配即拒,比等DOM解析快一个数量级 - JAXP解析器初始化时必须显式关闭:
parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)和parser.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true) - 若业务真需DTD(极罕见),只允许本地白名单路径(如
file:///opt/xsd/user.dtd),且禁用%参数实体和递归引用
XSD验证 ≠ SQL注入防护
XSD能保证<phone></phone>是11位数字,但防不住<phone>13800138000' AND (SELECT COUNT(*) FROM information_schema.tables) > 0--</phone>——这是合法XML,却是典型报错注入载荷。
- XSD验证开销大,带
xs:pattern的完整校验比纯DOM解析慢3–5倍,高并发下易成瓶颈 - 必须配合白名单过滤:对每个提取出的文本节点(如
username、email),额外做长度限制(如username≤ 32字符)、字符集限制(如仅允许a-z0-9_) - 数据库字段类型要严格对应:手机号存
CHAR(11)而非TEXT,整数字段不用VARCHAR存,避免隐式类型转换绕过校验
警惕XML字段被二次拼接进SQL字符串
最隐蔽的漏洞是:XML数据入库时被当CLOB安全存储,但某处报表导出逻辑又把它从数据库读出来,用CONCAT拼进WHERE条件——这等于把已入库的恶意内容重新送进SQL执行上下文。
- 检查所有含
XML字段的SQL语句,确认是否出现CONCAT、+、||等字符串拼接操作 - 禁止在SQL中调用任何XML解析函数(
UPDATEXML、EXTRACTVALUE、XMLPARSE)处理用户可控字段 - 如果必须用XPath提取,确保输入XML来自可信内部服务,且经过前述DOCTYPE拦截 + 解析器安全配置双重保护
真正难防的不是' OR 1=1这种明面payload,而是XML载荷混在正常业务流里,跨多个模块、多次序列化/反序列化后才触达SQL执行点——这时候靠单点过滤没用,得靠数据流向审计和参数隔离原则兜底。











