Referer校验如何防伪造

wen 开源项目 25

Referer校验如何防伪造?实战攻防与绕过策略全指南

目录导读

  1. 什么是Referer校验?它的核心作用是什么?
  2. Referer头真的能防伪造吗?常见攻击场景分析
  3. 黑客如何伪造Referer?3种主流绕过技术揭秘
  4. 服务器端如何正确实现Referer校验?
  5. Referer校验的局限性:为什么不能单独依赖它?
  6. 最佳实践:多层防护下的Referer校验正确姿势
  7. 常见问题问答(FAQ)

什么是Referer校验?它的核心作用是什么?

答: Referer(注:HTTP标准中正确的拼写是“Referer”,源于原始规范拼写错误)是HTTP请求头部的一个字段,它告诉服务器“当前请求是从哪个页面链接过来的”,服务器通过检查这个值是否与预期的来源匹配,来实现最基本的访问控制。

Referer校验如何防伪造

核心作用场景:

  • 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”

操作步骤:

  1. 拦截原始请求
  2. 将Referer头修改为任意值(如https://trusted-site.com/
  3. 转发请求

难度等级: 极低,任何会使用抓包工具的黑客都能在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 更严谨的域名检查

不要使用strposstrstr
错误写法:

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 优先级排序(从最重要到最次要)

  1. CSRF Token(不可绕过,但需要开发者正确实现)
  2. SameSite Cookie属性(设置为LaxStrict,可防止大部分CSRF)
  3. Referer校验(作为深度防御,可结合CORS策略)
  4. 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校验漏洞?

答:

  • 使用strpospreg_match做模糊匹配,被sub.example.com.attacker.com绕过
  • 没有处理空Referer,导致攻击者直接不带Referer访问
  • 允许file://协议的Referer(本地文件系统)
  • 没有校验端口(假设用的是标准80/443端口,但攻击者用非标准端口)

Q5:如何测试自己的Referer校验是否安全?

答:

  1. curl -H "Referer: " 测试空Referer
  2. curl -H "Referer: https://evil.com" 测试伪造来源
  3. 使用浏览器开发者工具,修改请求头后重放
  4. 用Burp Suite的Repeater修改Referer发送

最后建议:
不要在生产环境中单独依赖Referer校验,把它当作“锦上添花”的防卫层,而不是“一夫当关”的主力,真正可靠的方案是:CSRF Token + SameSite Cookie + 必要时的Referer辅助检查


(本文基于OWASP CSRF防护指南、Mozilla Referrer Policy文档及多家安全厂商的漏洞分析报告综合整理,确保内容符合搜索引擎收录规则并保持技术准确性。)

抱歉,评论功能暂时关闭!