上周三凌晨两点,只用我在一个企业后台的户输搜索框里敲了 ``,页面直接弹窗。地方都存洞这个后台有WAF,只用有CSRF Token,户输登录还要短信验证码。地方都存洞但搜索框把用户输入原样塞进了HTML里。只用
这不是户输个例。我做过六年渗透测试,地方都存洞经手过至少两百个系统。只用只要某个位置能让用户打字,户输它就有概率把恶意脚本当普通文字渲染出来。地方都存洞区别只是只用你找不找得到。
反射型:最没技术含量的户输那种
拿一个电商网站举例。它的地方都存洞搜索URL长这样:/search?q=手机。页面顶部会显示“为您找到‘手机’相关商品”。你改成 /search?q=,如果页面把这段脚本执行了,攻击者就能构造一个链接,伪装成正常搜索页发给受害者。点开,Cookie就被偷走。
这种漏洞的修复方式极其简单——输出时做HTML实体编码。但很多老系统用字符串拼接写前端,开发觉得“用户哪会输这种东西”。
存储型:一次注入,持续收割
论坛评论、用户昵称、收货地址、工单内容——这些数据存进数据库,再被其他页面读出来。如果读出来的时候没转义,脚本就永久留在那里。
我见过一个在线教育平台,学生提交作业的文本框没过滤。有个学生提交了 。之后每个批改这份作业的老师,后台会话都被偷走。攻击者用老师的权限改了成绩,还删了日志。整个攻击链只用了三行JavaScript。
DOM型:前端自己坑自己
有些页面不经过服务器。比如 location.hash 取URL片段,然后 innerHTML 直接写进页面。你访问 example.com/#,服务器日志里连这条请求都看不到,WAF也拦不住。因为脚本是在浏览器里动态生成的。
我调试过一个SPA应用,路由参数从 window.location.search 读出来,拼进模板字符串,再交给 v-html。开发觉得“这是前端路由,安全的”。结果任何一个带参数的页面都能弹窗。
为什么说“只要用户输入的地方都存在”
因为用户输入不只有表单。URL参数、HTTP头(比如User-Agent、Referer)、Cookie、文件上传的文件名、JSON字段、甚至WebSocket消息——任何能从外部进入系统的数据,只要最终出现在HTML上下文里,就有XSS风险。
我见过最离谱的案例:一个后台的“操作日志”页面,把User-Agent原样显示出来。攻击者伪造一个带脚本的User-Agent去访问首页,管理员一查日志,脚本执行。管理员权限被窃取。
还有PDF生成功能。用户提交的姓名被写进HTML模板,再转成PDF。姓名里带 ,PDF生成服务用的是无头浏览器,脚本在服务端执行。直接拿到服务器权限。
别指望框架能救你
React、Vue默认会转义,但开发者用了 dangerouslySetInnerHTML 或 v-html,防线就没了。Angular有Sanitizer,但有人用 bypassSecurityTrustHtml 绕过去。框架只是工具,写代码的人才是决定因素。
我现在的习惯:每看到一个输入框,就试三种payload——纯script标签、img onerror、svg onload。再检查URL参数、Referer头、上传文件名。十次里能中一次。这个比例在真实系统里已经很高了。
所以别问“这个系统有没有XSS”。要问“这个系统的哪个输入点还没被测试过”。答案通常是:全部。