📝XSS 和 CSRF 攻击
一、XSS
1. 概述
XSS(Cross Site Script)跨站脚本攻击,指的是攻击者向网站嵌入恶意脚本,使得用户在浏览该页面时执行这些脚本,从而达到窃取用户数据(如 cookie)的目的。
2. 分类
XSS 攻击可分为三种:存储型(持久型)、反射型(非持久型)、基于 DOM。
2.1 存储型
- 概述:存储型 XSS,也称为持久型 XSS,指的是:① 攻击者将恶意脚本提交到网站服务器的数据库中;② 当用户通过浏览器请求包含该恶意脚本的页面时;③ 恶意脚本会被执行,并将用户的 cookie 等敏感信息上传到攻击者的服务器。
- 示例:2015 年,喜马拉雅出现存储型 XSS 漏洞。黑客将恶意 JavaScript 代码作为专辑名称提交,因服务器未严格过滤,这段代码被存储在数据库中。当其他用户访问该专辑时,恶意脚本执行,窃取用户的 Cookie 信息,并上传到黑客的服务器。黑客因此可以非法访问用户账号。
2.2 反射型
-
概述:反射型 XSS,也称为非持久型 XSS,指的是:① 攻击者诱导用户点击一个恶意链接、提交表单或访问恶意网站;② 用户实际发送了一段包含恶意脚本的请求(即 XSS 代码出现在请求 URL 中)给网页服务器;③ 服务器解析请求并响应,响应内容中包含注入的恶意脚本;④ 浏览器在解析服务器响应时执行了恶意脚本,从而导致用户的 cookie 等敏感信息被窃取。
-
示例:假设服务端的路由和视图如下,
1
2
3
4
5
6
7
8
9
10
11var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express',xss:req.query.xss });
});
module.exports = router;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!DOCTYPE html>
<!-- index.ejs -->
<!-- 该视图将 URL 中的 xss 参数显示在页面 -->
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<div>
<%- xss %>
</div>
</body>
</html>当用户访问
http://localhost:3000/?xss=123
时,页面正常显示,但当用户访问http://localhost:3000/?xss=alert('你被xss攻击了')
时,页面会跳出一个弹窗,即恶意脚本被执行。
2.3 基于 DOM
-
概述:基于 DOM 的 XSS 攻击,指的是:① 攻击者通过各种手段(如网络劫持、在传输过程中修改 HTML 页面内容或诱导用户点击恶意链接等)将恶意脚本注入用户访问的页面中;② 这些恶意脚本在浏览器中执行,窃取用户的敏感信息。
-
示例:当用户访问以下链接,
1
http://example.com/?name=<script>alert('XSS攻击');</script>
如果网页的 JavaScript 直接将
name
参数插入到页面中而未进行过滤,恶意脚本会被执行,触发警报,显示“XSS攻击”,从而窃取用户的敏感信息。
3. 防御
-
根据恶意脚本是否要经过网页服务器处理,可以进一步将 XSS 攻击分为以下两种
-
服务端安全漏洞:存储型 XSS 攻击、反射型 XSS 攻击
-
前端的安全漏洞:基于 DOM 的 XSS 攻击
-
-
XSS 攻击的共性:① 向浏览器中嵌入恶意脚本 ② 再通过恶意脚本窃取用户信息发送给攻击者部署的恶意服务器上
3.1 服务端:用户输入 - 编码、解码和过滤
适用于存储型 & 反射型 XSS 攻击
-
编码:即将用户输入中的特殊字符(如
<
、>
等)进行字符实体编码。1
2
3
4// 用户输入
code: <script>alert('你被 xss 攻击了')</script>
// 用户输入的过滤结果
code: <script>alert(' 你被 xss 攻击了 ')</script> -
解码:为了确保某些内容可以按原样显示,需要对用户编码的内容进行解码。
-
过滤:即将用户输入中的不合法的内容(如 script、iframe 节点等)过滤掉。
1
2
3
4// 用户输入
code: <script>alert('你被 xss 攻击了')</script>
// 用户输入的过滤结果
code:
3.2 前端:内容安全策略(CSP)
概念
CSP(Content Security Policy)内容安全策略,指的是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本攻击(XSS 攻击)和数据注入攻击等。通过 CSP 你可以为浏览器配置一组安全策略,如 ① 指定有效域,即浏览器可以执行哪些来源的可执行脚本;② 指定有效资源,即浏览器可以加载哪些资源;等等。
兼容性:浏览器对 CSP 是向后兼容的,即:不支持 CSP 的浏览器会忽略 CSP 策略,而使用标准同源策略;对于支持 CSP 的浏览器,如果网页没有指定 CSP 策略,则也会使用标准的同源策略。
实现
CSP 可以 ① 通过配置服务器返回的 Content-Security-Policy
HTTP 标头;② 通过 <meta>
元素来进行配置并启用。
-
Content-Security-Policy
HTTP 标头的适用语法为:Content-Security-Policy: policy
-
<meta>
元素的使用语法为:<meta http-equiv="Content-Security-Policy" content=policy />
policy
参数是一个表示 CSP 策略的字符串,其包含一组 CSP 策略指令。
注-1:CSP 的某些功能(如发送 CSP 违规报告)仅在使用 HTTP 标头时可用。
注-2:
X-Content-Security-Policy
是旧版本的用于配置 CSP 策略的 HTTP 标头。
策略指令
CSP 策略由一系列策略指令所组成,每个策略指令都描述了针对某个特定资源的类型以及生效的范围。常见的策略指令为,
策略指令 | 含义 |
---|---|
default-src |
备选的源策略,适用于所有资源类型。 |
script-src |
允许执行的 JavaScript 脚本的源策略。 支持 <script> 这种通过 URL 加载的脚本,也支持通过 onclick 事件触发的内联脚本。 |
img-src |
允许加载的图片的源策略。 这里的所说的图片也包括网站图标 favicon 。 |
connect-src |
允许使用脚本请求的源策略。 这里的使用脚本请求的方式涵盖 WebSocket 、<a> 、fetch 、XMLHttpRequest 等 |
frame-src |
允许嵌套的 <iframe> 的源策略。 |
style-src |
允许加载的样式表的源策略。 |
object-src |
允许加载的 <object> 的源策略 |
每个策略指令允许指定一个或多个源,格式为 directive: <source>[ <source>]
常见的源的可选值为,
源值 | 含义 |
---|---|
<host-source> |
站点地址,由主机名(域名或 IP 地址) + 可选的协议名 + 可选的端口号组成,如 https://store.example.com 。 |
<scheme-source> |
协议名,可选 'http:' 或者 'https:' 等。必须带有冒号,不要有单引号。 |
'self' |
当前站点对应的源。这意味着,任何请求都必须发送到与当前站点相同的 URL(必须协议、域名、端口号都相同)。该取值必须有单引号。 |
'unsafe-inline' |
允许使用内联资源。包括内联脚本 javascript: URLs 、内联事件句柄、内联样式 style 。该取值必须有单引号。 |
'unsafe-inline' |
允许使用 eval() 或其他不安全的方法根据字符串创建代码。该取值必须有单引号。 |
'none' |
空集,表示没有任何源应该被匹配。该取值必须有单引号。 |
用例
功能 | 代码 |
---|---|
所有内容均需来自当前站点对应的源,不涵盖其子域名 | Content-Security-Policy: default-src 'self' |
所有内容均需来自受信任的域名及其子域名,域名不必须与当前站点相同 | Content-Security-Policy: default-src 'self' *.trusted.com |
所有内容均默认来自当前站点对应的源,除了 ① 图片可以从任意地方加载 ② 多媒体文件(音视频)仅允许从 media1.com 和 media2.com 加载(不包括子域名)③ 可执行脚本仅允许从 userscripts.example.com 加载(不包括子域名) |
Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com |
更多用法
CSP 可以通过 Content-Security-Policy-Report-Only
HTTP 标头部署为仅报告模式,此时 CSP 的策略并不是强制性的,但是任何违规行为将会报告给一个指定的 URL 地址,该 URL 地址通过策略指令 report-to
指定。更多:对策略进行测试。
注意:截止 2024-09-26,MDN 关于 CSP 的解释中,中英文版本略有差异,请以英文版本为准。
3.3 服务端: Cookie 设置 HttpOnly 标志
适用于所有 XSS 攻击
由于 XSS 攻击多用于窃取用户的 cookie 数据,为了阻止恶意脚本 document.cookie
获取 cookie 值,服务端可以给其返回的 cookie 携带 HttpOnly
标志。浏览器会禁止页面的 JavaScript 访问带有 HttpOnly 标志的 cookie。
服务端通过
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
响应一个携带 HttpOnly 标志的 cookie
二、CSRF
1. 概述
CSRF(Cross Site Request Forgery)跨站请求伪造,指的是攻击者冒充受信任的用户,向被攻击网站的服务器发送非预期请求。CSRF 会导致用户的登录态被盗用,攻击者甚至会冒充用户操作或修改用户数据。
2. 分类
CSRF 攻击可分为三种:Get 类型、Post 类型、链接类型。
2.1 Get 类型
-
概述:Get 类型的 CSRF,指的是:① 攻击者引诱用户进入恶意网站;② 恶意网站中自动向用户已登录的网站服务器发起
Get
请求,进行一些恶意操作。Get 请求是跨域的,因此攻击者常通过
<img>
、<script>
等标签绕过该限制 -
示例
1
2
3
4
5
6
7
8<!-- 当前页面是 attacker.com -->
<html>
<body>
<h1>黑客的站点: CSRF攻击演示</h1>
<img src="https://example.com/sendcoin?user=hacker&number=100">
</body>
</html>假设用户已登录
example.com
,攻击者引诱其打开attacker.com
。在该页面,攻击者将一个恶意链接(如转账请求接口)隐藏在<img>
标签中,欺骗浏览器认为这是图片资源。当页面加载时,浏览器会自动发起一个GET
请求。由于请求发送给example.com
时携带了用户的登录态(如 Cookies),服务器会将其视为合法的转账请求,导致用户财产遭受损失。
2.2 Post 类型
-
概述:Post 类型的 CSRF,指的是:① 攻击者引诱用户进入恶意网站;② 恶意网站中自动向用户已登录的网站服务器发起
Post
请求,进行一些恶意操作。Post 请求是跨域的,因此攻击者常通过
<form>
等标签绕过该限制,其中<form>
标签需要配合 JavaScript 自动提交 -
示例
1
2
3
4
5
6
7
8
9
10
11
12<!-- 当前页面是 attacker.com -->
<html>
<body>
<h1>黑客的站点: CSRF攻击演示</h1>
<form id= 'hacker-form' action="https://example.com/sendcoin" method=POST>
<input type="hidden" name="userll" value="hacker" />
<input type="hidden" name="numberll" value="100" />
</form>
<script> document.getElementById ('hacker-form').submit(); </script>
</body>
</html>假设用户已登录
example.com
,攻击者引诱其打开attacker.com
。在该页面,攻击者将一个恶意链接(如转账请求接口)隐藏在<form>
标签的action
属性中。该表单是一个隐藏的表单,当页面加载时,表单自动提交,浏览器会自动发起一个Post
请求。由于请求发送给example.com
时携带了用户的登录态(如 Cookies),服务器会将其视为合法的转账请求,导致用户财产遭受损失。
2.3 链接类型
-
概述:链接类型的 CSRF,指的是:① 用户点击攻击者提供的恶意链接;② 用户向已登录的网站发起了一个意料之外的
Get
请求,从而可能会涉及到一些恶意操作。 -
示例:链接类型的 CSRF 常见于邮件、图片、广告等形式。
1
2
3
4<div>
<img width=150 src=http://images.xuejuzi.cn/1612/1-161230185104_1.jpg/>
<a href="https://time.geekbang.org/sendcoin?user=hacker&number=100" taget="_bla点击下载美女照片"/>
</div>
3. 防御
因为 CSRF 是针对用户的请求伪造攻击,因此需要通过提高服务器的安全性来防护 CSRF 攻击。
3.1 服务端:Cookie 设置 SameSite 属性
我们知道,CSRF 攻击需要窃取用户的登录状态(通常为 cookie),然后冒充用户进行恶意操作。为了防止 cookie 在跨站请求中被发送,服务端可以为 cookie 配置 SameSite
属性,其取值及解释如下:
-
Strict
:浏览器仅在同一站点的请求中发送 cookie;如果请求来自不同的域名或协议,带有SameSite=Strict
属性的 cookie 将不会被发送。 -
Lax
:cookie 不会在跨站请求中发送,如加载图像或框架的请求;但当用户从外部站点导航到源站时(如点击链接),cookie 会被发送。这是未设置SameSite
属性时的默认行为。 -
None
:浏览器在跨站和同站请求中均会发送 cookie。设置该属性时,必须同时设置Secure
,如SameSite=None; Secure
。有
Secure
标识的 cookie 仅在使用 HTTPS 协议发送加密请求时才会被发送到服务器,这也就意味着:非安全站点(http:
)无法为 cookie 设置Secure
指令,因此也无法使用SameSite=None
。
一般的,为了防范 CSRF 攻击,需要设置 cookie 的 SameSite
属性的值为 Strict
或 Lax
。
服务端通过
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict 或 Lax 或 None
响应一个设置了 SameSite 属性的 cookie
3.2 服务端:验证 Referer 或 Origin 请求头
由于 CSRF 攻击多来自于第三方站点,从这个角度出发,可以在服务端进行某些操作禁止来自第三方站点的请求。一个可行的方式为,在服务端设置对请求报文中 Referer
或 Origin
HTTP 的请求头的检验,从而判断请求是否合法。这里对这两个请求头进行介绍,
-
Referer
:记录当前 HTTP 请求的来源地址(协议 + 主机 + 端口 + 路径)。1
Referer: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript # 请求来源的协议 + 主机 + 端口号 + 路径
注-1:由于
Referer
请求头记录的请求来源地址包括请求路径(path),因此可能会泄露用户隐私。注-2:当 ① 请求来源地址采用的协议为本地的
file
或data
② 当前请求的页面采用非安全协议,请求来源页面采用安全协议时,Referer
请求头不会被发送。 -
Origin
:记录当前 HTTP 请求的来源地址(协议 + 主机[ + 端口] or null)。1
2
3Origin: null # 表示请求来源是隐私敏感的
Origin: https://developer.mozilla.org # 请求来源的协议 + 主机
Origin: https://developer.mozilla.org:80 # 请求来源的协议 + 主机 + 端口号注-1:当请求是 ① 跨站请求 ② 除
Get
和Head
以外的同源请求时,Origin
请求头会被发送。注-2:相较于
Referer
请求头,由于不包含请求路径,Origin
请求头更加安全。 -
服务端校验逻辑:1.st 优先基于
Origin
字段进行请求合法性校验;2nd. 如果没有Origin
字段,则根据实际情况改为使用Referer
字段进行请求合法性校验。
3.3 前端:CSRF Token 验证
CSRF Token 的生成与验证过程可以分为以下两个步骤:
步骤 1:浏览器向服务器请求一个页面,服务器生成一个 CSRF Token,并将其嵌入在返回的页面中。
1 |
|
步骤 2:当浏览器需要向服务器发送其他请求时,必须携带页面中嵌入的 CSRF Token。服务器会验证该 CSRF Token 是否有效,从而决定是否响应该请求。
如果请求是从第三方网站发出的,则无法获取到 CSRF Token,因此即使发出了跨站请求,服务器也会因为 CSRF Token 不正确而拒绝该请求。
三、XSS Vs. CSRF
1. 攻击目标
- XSS:攻击者注入恶意脚本到网页中 → 窃取用户信息或劫持用户会话。
- CSRF:攻击者引诱用户在不知情的情况下向已认证的网站发送请求 → 利用用户的身份执行未授权操作。
2. 攻击类型
- XSS:存储型、反射型、基于 DOM 型
- CSRF:Get 类型、Post 类型、链接类型
3. 防护措施
- XSS:内容安全策略 CSP、输入输出过滤或转义、
HttpOnly
的 Cookie - CSRF:
SameSite
的 Cookie、Referer
/Origin
验证、CSRF Token 验证
References