XSS注入漏洞这玩意儿,暴露的位置跟大多数人的直觉正好相反。一提起来,大家条件反射去翻搜索框、表单、评论框,把输入过滤加了一遍又一遍,加完就觉得踏实了。可真被插进脚本的时候,问题十有八九出在输出那一环——数据从库里捞出来,顺手拼进页面,没人管它原来长什么样。

xss注入漏洞的爆点通常在输出环节
输入过滤本身没错,错在它兜不住底。用户能把东西送进来的口子太多了:表单可以绕开前端校验直接打接口,参数可以随便改,请求头里的 User-Agent 一样能被存下来。所以“我把输入框都过滤了”这句话放在审计报告里,基本等于没说。真正能兜住的是输出——同一份数据放到 HTML 正文、放到属性里、放到 script 里、拼进 URL,四种场景的转义规则完全不一样。一个昵称原样塞进 `<div>` 里,浏览器就当它是标签解析,事情就这么简单。
判断一个输出点危不危险,就问一句:这个变量到浏览器里,是当数据还是当代码。当数据的,老老实实编码;当代码的,除非是你亲手拼的常量,否则一律别放行。
存储型XSS,真正炸的是后台页面
反射型得骗人点链接,受害者多少要配合一下。存储型不用,它躺在数据库里等人来。最常见的一幕:用户提交的昵称、留言、订单备注、工单内容、甚至上传文件的名字,管理员一打开列表页,脚本就在管理员的浏览器里跑起来了。而管理员那个会话,权限通常比普通访客大得多,一个人中招,差不多等于后台敞开。
顺带提一句,日志和报表页也是输出点。有些统计后台会把来源 URL、搜索词原样渲染出来,你觉得那是内部页面,攻击者可不这么看。
编码要看上下文,一套htmlspecialchars走不了天下
htmlspecialchars 人人会用,可它只对 HTML 正文和带引号的属性有效。把用户输入拼进 `<a href=”…”>`,javascript: 伪协议它管不住;拼进 `<script>var a = ‘…'</script>`,一个单引号闭合就跑出去了;塞进 style 属性里,老浏览器照样能玩出花样。属性值一律加引号,这是最省事的一条规矩。URL 类型的输出先校验协议,白名单之外的一律不认。
SVG 也别忘。上传允许 SVG,差不多等于允许上传一个会执行脚本的 XML 文件,不少老系统就栽在这个不起眼的地方。
富文本编辑器,只有白名单才管用
只要站上有富文本——评论、文章正文、商品详情——就必然要放行一批标签。黑名单注定失败,onerror、onload、大小写变形、实体编码、双写标签,绕法能写一整页。老实上成熟的白名单库,比如 DOMPurify 这类,只放行要用的标签和属性,href 限死协议。自己拿正则去过滤 HTML,基本属于给自己安排返工。
修完怎么验?拿自己的测试账号,在昵称或者留言里存一段 `<img src=x onerror=alert(1)>`,再到列表页看它是被执行,还是被老老实实显示成一串字符。这招笨,但比看十份扫描报告直观。别拿别人的站当靶场,那是另一码事,也不合规。
框架会帮你转义,但留了几个后门
现代框架默认转义,这是好事,可后门就摆在明面上:Vue 的 v-html、React 的 dangerouslySetInnerHTML、Django 模板的 |safe、Jinja 的同名过滤器。这些标记一出现,等于作者亲手关掉了保护。上线前全库搜一遍这几个关键字,比什么扫描器都快。
老CMS和插件,漏点常常在这儿
虚拟主机上跑的站,多数是现成 CMS 加模板加插件。内核更新得还算勤,插件和主题常年没人管,一个下载量挺大的编辑器插件,可能好几年没维护过。虚拟主机给不了你改底层环境的权限,但目录权限和版本这些能自己收:上传目录、缓存目录别给执行权限,该 755 的别贪方便设成 777,PHP 版本跟着官方支持周期走,别为了兼容一个老插件一直钉在旧版本上。
WAF 能拦掉一批明显的 payload,别把它当成修复方案。它更像一层纱窗,挡蚊子不挡人。
HttpOnly和CSP,减损不治病
Cookie 加 HttpOnly,脚本就读不到会话;CSP 限制脚本来源,能让内联脚本失效。两样都值得做,但它们的作用是“降低影响”,不是“把洞堵上”。输出编码该做还得做。CSP 上线前先跑报告模式,一上来就全拦,站上的统计代码、客服组件全能给你拦没,然后你会以为是站点坏了。
顺序建议这么排:先把所有输出点列出来——模板里的变量、JS 里拼的字符串、后台列表页、导出页;逐个对照上下文看转义;再把富文本和上传功能单独过一遍;最后补 HttpOnly、CSP 和目录权限。设备可以晚点买,这几行代码的事先做完。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!