01
引 言
登录态、检索框、评论区、后台配置页、第三方回调——Web 业务的这些环节大多在浏览器里完成。用户和系统之间来回交换的,无非是一段段字符串;系统安不安全,取决于这些字符串会不会被浏览器当成可执行内容。跨站脚本攻击(Cross-Site Scripting,XSS)盯的正是这一点:攻击者把不可信数据送进页面的渲染路径,让恶意脚本在受害者的浏览器里执行。[1]
XSS 攻击长期出现在各类安全榜单与漏洞披露里,倒不是因为技术有多新,而是前端交互越来越密、输出点越来越散,展示的数据稍一疏忽,就可能写进带脚本语义的位置。只核对某一次过滤规则写没写全,不能有效防止XSS。更稳妥的办法,是沿着链路从头看一遍:数据从哪里进来,服务端怎么处理、有没有落库,最后怎么回到响应里,又在哪一段 HTML 或 JavaScript 上下文里被解析。
02
XSS是什么:不可信数据被当成可执行内容
Web 应用把攻击者可控的数据输出到页面,又没有充分隔离,受害者的浏览器把它当成脚本或可执行标记来处理,XSS 就成立了。[2] 直接后果大多落在用户身上:会话标识被窃取、页面内容被篡改、用户被诱导跳转到钓鱼站点,还可能和其他缺陷叠加,把危害进一步放大。
攻击的关键在输出阶段——数据没有按所在上下文被当成纯文本。同样一份用户输入,有的只该出现在段落文字里,有的会进入标签属性,有的会被拼进内联脚本或 URL。解析规则随上下文而变,防护也得跟着变。如果把问题简单理解成“某个危险字符没删干净”,就很容易漏掉这种差别。XSS攻击真正的产生原因是:服务端或前端把本不该有脚本语义的数据,放进了有脚本语义的位置,数据的信任边界和渲染边界没有对齐。
攻击是在受害者的浏览器里执行的。应用日志里不一定有异常进程,数据库也未必被拖库,但业务会话、页面展示、用户操作可能都已经被劫持。所以防护不能只拦请求,还要管输出、管上下文;测试也要同时覆盖服务端回显和前端 DOM 操作这两条路。

图1 XSS 攻击示意:不可信数据被当作可执行代码
03
从输入到渲染:风险如何走完这条链路
可以把XSS 拆成四个阶段,看一份输入怎么一步步变成可执行渲染。风险很少只落在某一次函数调用上;数据跨层传递时,会逐渐获得本来不该有的语义。
第一是入口阶段,数据的来源很散:URL 查询参数与路径片段、表单字段、Cookie 与部分请求头、富文本编辑器内容,还有第三方跳转带回来的参数。只要这些数据最终可能出现在页面上,就该默认它不可信。入口越多,攻击面越散,旁路越容易漏。登录表单加固了,错误页还是可能把异常信息回显出来;移动端 H5 也可能对分享链接的参数做二次拼接。
第二是服务端阶段,常见的是原样回显,或者落库之后再读出来。搜索词直接写进结果页标题,内容写进数据库后再被列表页读出,模板字符串拼接,错误页把异常信息回显给用户等等情况,都是这一类。脚本未必在这一步执行,但载荷能否存活、能否跨请求复现,往往在这里就决定了。存储型问题尤其依赖这一阶段:一旦写进去,之后每一次正常访问都可能成为触发点。
第三是组装响应阶段。数据可能落在 HTML 正文、属性值、嵌入的 JavaScript 字符串里,也可能以 JSON 返回,再由前端拼进 DOM。很多漏洞并不出现在第一次写入,而是某次组装响应时少做了一次正确的编码。前后端分离的架构下,后端常常觉得“我只返回 JSON”,前端却在渲染层把字段直接写进 innerHTML 这类接口,风险随即转移到了浏览器端。
最后是浏览器阶段。HTML 被解析,DOM 被构建起来,脚本在合适的时机执行。可能把字符串变成脚本语义的 API 或标记位置,就是危险的汇聚点(sink),比如不安全地把字符串当作 HTML 插入,或者把没编码的内容写进可执行上下文。同一份输入,落在标签体、属性、URL、JS 字符串还是 CSS 里,需要的安全处理各不相同,所以一律采用 HTML 实体编码,经常会失效。[3]
顺着这条链路排查,最后通常归结为三个检查点:数据是否落到页面(无论参数回显还是从存储读出)、数据所落到的 DOM 位置,以及该位置能否突破上下文并引入可执行结构。自动化检测和人工审计,本质上都是在回答这三点。测试用例针对这些要点来设计,比盲目堆特殊字符更接近真实风险。

图2 XSS风险链路:从输入到渲染
04
三类XSS:反射型、存储型与DOM型
XSS 通常分三类:反射型、存储型和基于 DOM 的类型。这三类恰好落在上述链路的不同阶段,触发时机和排查重点各不相同。[1][2]
反射型:恶意数据出现在当前请求里,经服务端处理后反射回响应页面,随即在浏览器中执行。典型场景是搜索页、错误提示页、带参数的跳转页。载荷不一定入库,问题可能只在特定请求下出现;但链接一旦被诱导点击,危害同样真实。排查时看请求参数有没有经过充分的编码,再回显到 HTML 可执行上下文。
存储型:先把恶意数据写进去,之后在别的请求里被取出来渲染。评论、公告、用户昵称、工单备注、日志展示后台,都是常见的持久化位置。受害对象可能是管理员,也可能是其他用户,影响面通常更大。除了输入接口,还要清点所有读取并展示这个字段的模板与前端组件——写入点修了,读出点留下一处旧模板,漏洞就还在。
DOM 型:关键逻辑发生在浏览器端。页面脚本从 location、document.referrer、postMessage、前端路由状态等处读取数据,再通过 innerHTML 或不恰当的模板拼接写进 DOM,在本地完成从“污染”到“执行”。服务端响应里未必看得到完整载荷,只静态看后端模板也可能漏检,得顺着前端数据流,看 source 到 sink 怎么传。
下表从触发路径与排查侧重点做简要对照:
表1 三类XSS对照

05
常见渲染上下文与工程误区
已经做了转义处理,仍然发生XSS攻击,是因为使用错误的方法。HTML 文本、标签属性、事件处理器、JavaScript协议URL、内联脚本里的字符串,解析和编码规则各不相同。把给 HTML 正文准备的实体编码原样用到 JavaScript 字符串里,或者把本该做属性编码的内容放进 href,都可能留下可执行的缝隙。[3] 安全编码必须按上下文选规则,不能用一套替换应付所有场景。
常见误区有这几个:
1. 只拦截script标签,却忽略事件属性与其他可执行载体;
2. 只在前端做校验,并假设所有请求都来自合法浏览器;
3. 对所有输出统一做一种编码;
4. 在现代框架中主动关闭默认转义、或使用会解析HTML的危险API。
预防时,按输出上下文做编码;富文本走白名单净化,能用纯文本展示就尽量不插入 HTML。
纵深防御仍有价值。内容安全策略(Content-Security-Policy,CSP)能限制脚本来源与内联执行;给 Cookie 加上 HttpOnly 等标志,能降低脚本直接读取会话标识的风险。[4] 但这些只是减损和加固,替代不了正确的输出处理。缺了上下文编码,只靠 CSP 或 HttpOnly,还是可能留下展示篡改、钓鱼诱导的空间。浏览器解析 HTML 的细节一直在变,工程上应以权威规范、成熟库为准,而不是凭经验推测某个字符一定安全。[5]
06
防护与检测:把关口放在正确的位置
输入校验能收窄非法形态,却挡不住旁路进来的不可信数据。真正有效的预防手段在输出侧:按上下文编码,尽量用框架默认的转义。对于富文本这类必须保留 HTML 的场景,应借助持续维护的白名单实施净化,而不是自行编写黑名单勉强处理。评审模板和前端组件时,凡是把字符串变成HTML 或脚本的汇聚点都标出来,每个字段落在什么输出上下文也应该写清楚。
运维和平台侧可以配合:安全响应头、依赖与模板审计、定期回归测试。发布流程把 CSP 等策略纳入基线检查,能减少“功能上线了、加固头忘配了”的情况。历史系统按模块梳理高风险页:搜索与错误页(反射)、UGC 与后台展示(存储)、大量前端路由拼装(DOM)。
针对测试,不能只关注响应里有没有特殊字符,还要看这些字符落在哪种 DOM 语义位置、有没有条件冲破上下文、引入可执行结构。覆盖“输入入口、回显点、上下文”的测试设计,比单纯堆载荷更接近真实风险。团队规范也应该跟上:默认转义不要随意关闭;新增展示字段先问上下文;安全用例覆盖反射、存储和前端 DOM 三条路径;缺陷验收核对编码是否匹配上下文,而不是某个关键字有没有被过滤掉。
07
结 语
XSS 的风险贯穿 Web 应用从输入到渲染的全过程。认清不可信数据怎么进系统、怎么在响应里落位、怎么在浏览器里获得脚本语义,才能把防护落到正确的编码与隔离策略上,把检测对准真正的危险汇聚点。没有一劳永逸的开关,开发规范、运行时加固和持续测试,需要一起推进。
上海控安面向车载、工控等联网与嵌入式系统,推出了 SmartRocket TestSec 自动化智能模糊渗透测试工具,提供一体化的安全测试能力。平台以协议模糊测试与渗透测试为核心,覆盖有线、无线等多类通信场景,支持故障注入与自定义扩展,并配套被测件与硬件环境配置,以及流量仿真、监控与解析等能力,可以在接近真实的链路里开展验证。在此基础上,还能延伸到 Web、操作系统、应用与数据库等对象,帮助团队在研发和验收阶段更早发现稳健性与安全风险。
TestSec 具备 Web 测试能力。用户可以针对目标站点创建 Web 测试项目,管理待测 URL(支持爬取),再对目标发起 Web 模糊测试,测试过程中能发现包括反射型跨站脚本(reflected-xss)在内的多类 Web 安全缺陷。缺陷管理里可以查看目标站点、问题 URL、相关 HTTP 报文、污点传播链和修复建议,还能导出 Web 模糊测试分析报告,方便研发与测试团队定位不可信输入是怎么传到危险输出点的。

图3 TestSec Web测试缺陷详情界面示意
从软件行为到协议与 Web 应用层,立体化的安全测试有助于在上线前后尽早暴露输入到渲染链路上的薄弱环节。防护知识要更新,更要用可重复的工程手段,验证防护是不是真的生效。
参考文献
[1] OWASP Foundation. Cross Site Scripting (XSS)[EB/OL]. https://owasp.org/www-community/attacks/xss/
[2] MITRE. CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')[EB/OL]. https://cwe.mitre.org/data/definitions/79.html
[3] OWASP Foundation. XSS Prevention Cheat Sheet[EB/OL]. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
[4] MDN Web Docs. Content Security Policy (CSP)[EB/OL]. https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[5] WHATWG. HTML Living Standard[EB/OL]. https://html.spec.whatwg.org/
转载自 CSDN-专业IT技术社区




