【XML】基础篇
XML 含义
让任何两个系统无论语言、平台、版本差异,都能用同一套自描述的标签树完成“读-写-校验-扩展”的完整闭环。
Java 的 web.xml、Spring 的 application-context.xml、Maven 的 pom.xml 之所以选择 XML,并非开发者偏爱括号,而是需要一种“既能被人类 diff,又能被工具校验”的层次结构;相比之下,properties 或 yml 虽然简洁,却缺少 Schema 这一层硬性约束,难以在构建阶段就拦住拼写错误。另一方面,系统与系统之间的远程调用要求“强契约”,XML 凭借 DTD 与 XSD 两份“合同”先验地告诉对方:根元素必须叫什么、子元素出现顺序、属性是否允许为空、默认值是多少,甚至数据类型是 string 还是 decimal——这些在 JSON 里要靠运行时 if 判断,在 XML 里只需解析器一次校验。
DTD
DTD 用简洁的 !ELEMENT
、!ATTLIST
语法把「TranInfo 必须依次包含 CdtrInf、DbtrInf、Amt」写成几行声明,好处是短小、易读、可直接嵌在 XML 头部;坏处是数据类型只有 CDATA 一种,无法表达整数、日期、正则,更不支持命名空间。于是 XSD:xs:string、xs:decimal、xs:date 精确到原子类型,xs:sequence、xs:choice、xs:all 描述顺序与出现次数,xs:restriction 还能加正则约束。
XML 的宏替换机制
内部实体像 #define
,把 &cs;
展开成 "changsha";外部实体则像 #include
,通过 SYSTEM "file:///..."
或 PUBLIC "http://..."
把另一份文件内容整体拉进来。浏览器出于安全考虑禁止外部实体,但 PHP、Java 等解析器默认开启,于是诞生 XXE(XML External Entity)攻击:把 &xxe;
指向 file:///etc/passwd
或内网 http://10.0.0.1/admin
,一次解析就造成读文件、SSRF、拒绝服务多重危害。
给出 PHP 环境下常见的「协议矩阵」:file、php、http、https、ftp、zip、expect 等,每种协议都能被外部实体或 simplexml_load_file()
调用。php://filter/read=convert.base64-encode/resource=index.php
能把源码 Base64 后回显,规避了短标签被解析的风险;expect://id
直接执行系统命令,但需安装 expect 扩展;zip:// 和 rar:// 允许把上传的压缩包当成文件系统挂载,从而包含里面的 .php
木马。这些协议组合起来,让 XML 不再只是数据载体,而是变成一把远程文件系统与命令执行的万能钥匙。
从 DTD 到 XSD,从内部实体到外部实体,从配置文件到攻击载荷,XML 用层层嵌套的括号,既是开发者手里的契约,也是攻击者眼中的入口。理解它的约束机制与协议矩阵,就等于同时掌握了「写合同」与「撕合同」的两面技巧。