|
一个搜索框,你输入什么它就回显什么,看起来人畜无害。但你把鼠标移到搜索结果上,状态栏显示的URL里,#号后面跟着一串编码过的字符。你随手改了其中一个参数,页面内容立刻变了。这不是服务端的问题,浏览器地址栏里的#号之后的一切,都只是前端自己的事——这就是DOM型XSS的舞台。 服务端XSS像有人撬你家门锁,而DOM型XSS更像是你家保姆被收买了,她主动把钥匙递给了陌生人。攻击者根本不用碰你的服务器,他把恶意代码藏在URL的#号后面,或者藏在location.hash、document.referrer、window.name这些前端能读到的“本地输入”里。你的页面JavaScript一旦把这些数据当作HTML片段插进DOM,恶意脚本就活了。 我见过一个真实案例。某个在线文档站,分享文档功能把文档标题写在location.hash里,然后直接用innerHTML拼到页面上。攻击者只需要发给受害者一个链接,#号后面是<img src=x onerror=alert(document.cookie)>,标题回显时那段代码就被执行了。整个过程服务器没有收到任何异常请求,日志干净得能当镜子用。安全防护全在服务端检查输入,压根没料到危险藏在URL的碎片标识里。 这类漏洞最麻烦的地方在于,它绕过了所有基于网络层的防护。WAF看不到它,服务端过滤也管不着它,因为攻击代码从没到过服务器。开发者的逻辑是“我后端已经过滤了所有参数”,可前端自己从location对象里取数据,等于绕过后端开了个后门。更糟的是,很多单页应用框架(SPA)大量依赖hash路由,这给了DOM XSS天然的藏身处。 检测它比利用它难。你没法用扫描器简单爬几个URL就发现,因为触发点往往在用户交互之后。代码审计得盯着innerHTML、document.write、eval、setTimeout的字符串参数,还得追踪这些函数的输入是否来自location、document.cookie等可被外部控制的来源。一个常见的坑是:开发者以为用了textContent就安全了,但某个第三方库内部却用了innerHTML去处理同一个值。 修复思路其实很朴素:别信任任何能在浏览器里被改写的“变量”。URL的hash、search、甚至document.referrer,全都不可信。输出到HTML时,用textContent代替innerHTML,或者用框架自带的转义机制。如果你非要用HTML片段,那就手动做白名单过滤,把所有标签和属性列出来,只留安全的。 对了,还有个冷知识:javascript:协议在#号后面也能执行。你把整个URL改成javascript:alert(1),如果页面代码直接拿location.hash去赋值给某个a标签的href,点击后照样弹出窗口。这种细节,光靠自动化工具根本测不出来。 说到底,DOM型XSS考验的是开发者对“数据边界”的理解。服务器管不了浏览器地址栏里那串字符,前端代码才是最后一道防线。把每个location对象的使用都当成用户输入来对待,比什么都管用。 |