页面上冒出来的那对尖括号,多数时候是模板自己吐的,可只要有一个是访客敲进去的,性质就变了。xss漏洞原理就一句话:输入进了输出,中间没按上下文转义,浏览器就当代码跑。后端分不清哪段是它拼的、哪段是别人填的,浏览器也不替它分。这条链路在哪一环断了,注入点就在哪。下面按我这些年翻代码、看日志的路子捋,不讲分级表,讲能上手查的东西。

xss漏洞原理:浏览器只按语法解析,不认作者
浏览器拿到一段 HTML,只按语法解析,它不会去分辨这段是程序生成的还是访客填的。你塞进去,它就跑。所以防这件事的核心不在”拦”,在于让输入永远以数据身份出现,别混进语法结构里。听着绕,落到代码上就是:输出的时候按所在位置转义,别原样拼。
转义还得看位置。同一段字符串落在 HTML 正文、属性值、JS 字符串、URL 参数里,需要的处理各不一样。见过有人写了个全局过滤函数就撒手了,结果属性里的引号一闭合,照样跑起来。所以别问”过滤了没”,要问”这段落在哪、按哪种规则转的”。
存储型、反射型、DOM 型这三个叫法,是行业里图沟通方便分出来的,不是按危害排的座次。真出事的时候三种都能把人折腾够呛,区别只是你从哪儿开始查、日志上留没留下痕迹。
存储型:先进库,再分给每个访客
留言、评论、昵称、个人签名、后台备注,这类内容先进数据库,之后又被渲染到页面上,就是存储型。它不用骗人点链接,谁打开那个页面谁中,危害面最大。一个长度限制做了、字符处理没做的昵称字段,前端显示时又直接拼 HTML,这个组合就够了。
富文本编辑器更麻烦。黑名单式的过滤挡不住 <img onerror> 这类写法,标签属性里能塞的位置太多,写黑名单等于跟整个 HTML 规范对赌。这块要么上成熟的白名单库,要么干脆只允许纯文本,别自己发明规则。
还有个场景经常被跳过:后台。很多人只管前台,后台列表页照样把用户提交的内容原样渲染,管理员一登录就中招。后台往往还带着更高的权限,这一下损失就不是一个页面的事了。
反射型:参数原样吐回页面
搜索词、错误提示、跳转告知、分页参数,这类”你刚输的东西”最常被原样回显。URL 里带上尖括号和引号的字符串,服务端没转义就写进结果页标题,浏览器就执行了。反射型要诱导点击,所以经常配短链、二维码,或者群里来一句”帮忙看下这页面怎么打不开”。
判断手法土但有效:随便输点带尖括号、带引号、带括号的字符串,提交完看返回的 HTML 源码里是不是原样躺着。原样躺着的,八成没做转义;变成实体编码的,至少这一步处理了。别只看页面显示效果,显示一切正常不代表源码干净。
DOM 型:服务端日志干净得像没事
这一类最容易被漏掉。前端 JS 从 location.hash 或者 location.search 取参数,再往 innerHTML、document.write、eval 一类的地方塞。请求根本没到后端,WAF 上没有记录,访问日志也一片正常,扫描器报告还给你一片绿。
查这种只能翻前端代码:搜 innerHTML、insertAdjacentHTML、eval、setTimeout 传字符串这些字眼,再看看等号右边那个值能不能被 URL 控制。能控制又没处理,就是一条。改起来不难,往页面里放文本用 textContent,非要拼结构就走白名单,别图省事直接拼字符串。
防御这活儿,做一次过滤远远不够
输入侧做长度、类型、字符集校验,输出侧按上下文转义,这两件事不能互相替代。有人觉得入口已经校验过了,输出就随手拼,结果绕过方式比想象的多。入口校验是为了业务合理性,输出转义才是防线。
Cookie 上挂 HttpOnly,能减少被脚本读走的风险,但它不是万能的,页面上照跑不误。CSP 能挡掉一部分执行,可配置太松等于没配,也别拿它当唯一手段。多层叠着用,才是正常思路。
用框架的注意一点:自带的自动转义别随手关掉。为了图方便关一次,日后补回来要翻遍模板。第三方统计、客服挂件、广告脚本这些外链,也得当成潜在注入源看待,能走沙箱就走沙箱。
自查清单,按顺序过一遍
先列所有会回显用户内容的位置,一个一个对着源码看;再看富文本允许的标签白名单,是不是真按白名单执行;后台页面单独过一遍,别跟前台混在一起;前端搜一遍拼 HTML 的写法;最后把第三方脚本清点一遍,谁有权往页面里写东西。
清单过完别急着收工,改完一处就拿带尖括号和引号的字符串复测一次。安全这事没有”改完就完”的说法,版本迭代、运营加字段、前端换组件,任何一次改动都可能重新开一个口子。每次上线前把这几个点扫一眼,比事后补救省心得多。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!