Referer校验如何防伪造?实战攻防与绕过策略全指南
目录导读
- 什么是Referer校验?它的核心作用是什么?
- Referer头真的能防伪造吗?常见攻击场景分析
- 黑客如何伪造Referer?3种主流绕过技术揭秘
- 服务器端如何正确实现Referer校验?
- Referer校验的局限性:为什么不能单独依赖它?
- 最佳实践:多层防护下的Referer校验正确姿势
- 常见问题问答(FAQ)
什么是Referer校验?它的核心作用是什么?
答: Referer(注:HTTP标准中正确的拼写是“Referer”,源于原始规范拼写错误)是HTTP请求头部的一个字段,它告诉服务器“当前请求是从哪个页面链接过来的”,服务器通过检查这个值是否与预期的来源匹配,来实现最基本的访问控制。

核心作用场景:
- CSRF(跨站请求伪造)防护:银行转账接口只允许来自
bank.example.com的请求 - 防盗链:图片资源仅允许来自
cdn.example.com的页面引用 - 防止跨站数据窃取:API接口只允许特定的前端域名调用
真实案例: 某电商网站的“修改收货地址”API,只校验了Referer是否为www.shop.com,攻击者只要在恶意页面中构造一个包含该Referer值的请求,就能绕过防护。
Referer头真的能防伪造吗?常见攻击场景分析
1 为什么很多人认为Referer是可信的?
早期的Web开发教材常常将Referer校验列为CSRF防护的“标准做法”,但实际上,Referer头完全由客户端(浏览器)控制,浏览器允许开发者通过某种方式修改它。
2 攻击场景举例
图片标签CSRF攻击
攻击者在论坛帖子中插入:
<img src="https://bank.com/transfer?to=attacker&amount=10000" />
当用户访问该帖子时,浏览器会自动发送请求,Referer是forum.com,如果银行只允许bank.com的Referer,攻击失败。
表单提交CSRF
如果攻击者能通过<form>标签构造请求,并利用JavaScript动态设置Referer(部分旧浏览器支持),就能绕过校验。
问题本质: Referer校验的强度完全不比你在代码中写的那行if($_SERVER['HTTP_REFERER'] !== 'expected') { die(); }更强,它只能对抗最基础的、未经过精心构造的攻击。
黑客如何伪造Referer?3种主流绕过技术揭秘
1 方法一:使用<meta>标签或rel="noreferrer"
虽然现代浏览器默认不发送Referer的情况越来越多,但攻击者可以强制不发送Referer来绕过“必须包含特定域名”的校验。
绕过原理:
如果服务器代码写的是:
if (strpos($_SERVER['HTTP_REFERER'], 'example.com') === false) { 拒绝; }
攻击者可以确保请求完全不带Referer头,此时$_SERVER['HTTP_REFERER']为空字符串或者不存在,如果代码没有针对空值做特殊处理(比如用isset()判断),就会直接通过校验。
攻击构造:
<a href="https://victim.com/action" rel="noreferrer">点击领奖</a>
或者使用<meta name="referrer" content="no-referrer">对整个页面生效。
2 方法二:通过浏览器扩展或代理工具修改
工具举例:
- Burp Suite的Repeater模块
- Fiddler的自动响应规则
- Chrome扩展如“Modify Header Value”
操作步骤:
- 拦截原始请求
- 将Referer头修改为任意值(如
https://trusted-site.com/) - 转发请求
难度等级: 极低,任何会使用抓包工具的黑客都能在5秒内完成。
3 方法三:利用302跳转“合法污染”
经典绕过技术:
攻击者在自己的恶意站点上添加一个302跳转页面,假设攻击者域名是evil.com,服务器端代码:
header('Location: https://victim.com/transfer?to=hacker&amount=1000');
当用户访问evil.com/redirect.php时,浏览器向victim.com发起的请求的Referer是evil.com,但如果攻击者在evil.com上放置一个指向victim.com的链接,中间没有任何跳转,Referer就是evil.com,无法通过校验。
改进绕过: 利用<form>的action属性,结合JavaScript动态提交,某些浏览器在form提交时,Referer可以设置为document.referrer(即上一个页面的URL),如果攻击者先让用户访问一个“合法的”第三方页面(该页面允许跨域跳转),然后再跳转到攻击页面,Referer就可能变成那个合法页面。
注意: 现代浏览器(Chrome 85+,Firefox 87+)已经加强了Referrer Policy,默认情况下跨域请求的Referer只会发送origin部分(协议+域名+端口),而不是完整路径,但这依然无法阻止“伪造源域名”的攻击,因为origin就是evil.com,除非攻击者能找到与victim.com同域的来源。
服务器端如何正确实现Referer校验?
1 正确的校验逻辑(PHP示例)
function checkReferer() {
$expected = 'https://www.example.com';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
if (empty($referer)) {
// 方案A:拒绝空Referer(严格模式)
http_response_code(403);
die('Referer missing');
// 方案B:允许空Referer(宽松模式,不推荐)
// return true;
}
$parsed = parse_url($referer);
$sourceHost = $parsed['scheme'] . '://' . $parsed['host'];
// 只要来源域名完全匹配
if ($sourceHost !== $expected) {
http_response_code(403);
die('Invalid referer');
}
return true;
}
2 更严谨的域名检查
不要使用strpos或strstr!
错误写法:
if (strpos($referer, 'example.com') !== false) // 会被 evil-example.com 绕过
正确写法:精确比较域名部分,包括https/http协议。
3 结合CSRF Token才是王道
服务器端增加Token校验:
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
// 在表单中嵌入字段
echo '<input type="hidden" name="csrf_token" value="'.$token.'">';
// 接收时校验
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
// 合法请求
} else {
die('CSRF token mismatch');
}
Referer校验的局限性:为什么不能单独依赖它?
1 浏览器Referrer Policy差异
- Same-origin:只发送同源请求的完整Referer(跨域不发送)
- strict-origin-when-cross-origin:同源发完整,跨域只发origin
- no-referrer:永远不发
如果你的服务器严格要求非空Referer,而用户浏览器设置了no-referrer策略(例如通过HTTPS跳转到HTTP),那么所有合法用户的请求都会被拦截,造成可用性问题。
2 浏览器插件/隐私模式的影响
- 许多浏览器插件会默认移除Referer
- 隐私模式(如Brave浏览器的Shields)会剥离Referer
- 移动端WebView(如微信内置浏览器)可能完全不发送Referer
3 分布式架构下的复杂场景
- 反向代理:如果Nginx或Cloudflare没有正确透传Referer,服务器收到的可能是代理的IP
- CDN缓存:静态资源请求的Referer可能被CDN修改
Referer校验只能作为辅助手段,绝不可单独依赖,适合用在“非关键操作”的轻量防护上,比如防盗链、日志统计。
最佳实践:多层防护下的Referer校验正确姿势
1 优先级排序(从最重要到最次要)
- CSRF Token(不可绕过,但需要开发者正确实现)
- SameSite Cookie属性(设置为
Lax或Strict,可防止大部分CSRF) - Referer校验(作为深度防御,可结合CORS策略)
- IP限制(仅允许内网IP访问敏感接口)
2 正确的实现示例(Nginx层 + 应用层)
Nginx层先过滤:
location /api/transfer {
if ($http_referer !~* '^https://www\.example\.com') {
return 403;
}
proxy_pass http://backend;
}
应用层再校验Token:
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'])) {
http_response_code(403);
exit;
}
3 针对“无Referer”的友好处理
不要直接拒绝所有空Referer请求!建议:
if (empty($referer)) {
// 记录日志,但仍然允许通过(假设Token校验已通过)
error_log("Warning: Request from $ip has no referer");
}
原因: 许多不执行JS的请求(如邮件阅读器、RSS客户端)都不会发送Referer,拒绝它们会导致用户无法正常使用。
常见问题问答(FAQ)
Q1:Referer校验到底能不能防住CSRF?
答: 不能长期有效,它只能挡住最“初级”的攻击(比如直接通过<img>标签发起的CSRF,且攻击者无法修改Referer的情况),现代黑客可以通过浏览器扩展、302跳转、meta标签等轻松绕过。
Q2:如果必须使用Referer校验,如何提高安全性?
答:
- 做严格的域名匹配(包括协议和端口)
- 拒绝空Referer时需配合Token,否则不要直接拒绝
- 记录所有异常Referer到日志,用于监控攻击行为
- 配合CORS的
Access-Control-Allow-Origin头,只允许特定源
Q3:为什么HTTPS页面不能向HTTP接口发送Referer?
答: 这是浏览器的安全策略(Referrer Policy的strict-origin),防止HTTPS页面的敏感路径泄露到不安全的HTTP连接,如果你的网站采用HTTPS,建议所有接口也使用HTTPS。
Q4:有哪些常见的Referer校验漏洞?
答:
- 使用
strpos或preg_match做模糊匹配,被sub.example.com.attacker.com绕过 - 没有处理空Referer,导致攻击者直接不带Referer访问
- 允许
file://协议的Referer(本地文件系统) - 没有校验端口(假设用的是标准80/443端口,但攻击者用非标准端口)
Q5:如何测试自己的Referer校验是否安全?
答:
- 用
curl -H "Referer: "测试空Referer - 用
curl -H "Referer: https://evil.com"测试伪造来源 - 使用浏览器开发者工具,修改请求头后重放
- 用Burp Suite的Repeater修改Referer发送
最后建议:
不要在生产环境中单独依赖Referer校验,把它当作“锦上添花”的防卫层,而不是“一夫当关”的主力,真正可靠的方案是:CSRF Token + SameSite Cookie + 必要时的Referer辅助检查。
(本文基于OWASP CSRF防护指南、Mozilla Referrer Policy文档及多家安全厂商的漏洞分析报告综合整理,确保内容符合搜索引擎收录规则并保持技术准确性。)