Web安全:你所不知道的HTTP Referer注入攻击
文章目录
- 引言
- 什么是Referer头
- 总结:Referer 头的要点
- Referer头注入的前置条件
- 什么是 Referer 头注入
- 什么是 Referer 头SQL注入
- 实战演练
- 防御之道:如何防御Referer头注入
引言
设想一个场景——“一个看似正常的下载日志功能,却成了攻击者窃取用户敏感文件的通道。这一切,都源于一个被轻易信任的HTTP头——Referer”
很多开发者对Referer头只知其然(用于来源统计、防盗链),而不知其所以然,更忽略了其潜在的安全风险。今天,我们就来揭开Referer头注入的神秘面纱
什么是Referer头
Referer(来源页)是一个 HTTP 请求头字段,用于告知服务器,当前请求是从哪个页面或哪个网址发起的
简单来说:当你在浏览器中从页面 A 点击一个链接跳到页面 B,或从页面 A 加载一个图片、脚本等资源时,浏览器在向页面 B 或资源服务器发送请求时,会自动在请求头中加入 Referer: [页面A的URL],告诉对方:“我是从那个页面过来的”
举个例子
- 你在百度(www.bing.com)搜索了一个关键词
- 在搜索结果页中,你点击了一个指向“b站”(www.bilibili.com)的链接
- 你的浏览器在向哔哩哔哩服务器发送请求时,会在 HTTP 请求头中包含类似这样的信息:
GET /article/123 HTTP/1.1
Host: www.bilibili.com
Referer: https://www.bing.com/s?wd=关键词
- b站的服务器收到请求后,通过查看 Referer 头,就能知道你是从必应搜索过来的
Referer 的拼写错误
这是一个有趣的历史事实。HTTP 规范中这个字段的正确拼写是 Referer,而不是正确的英文单词 Referrer
这个拼写错误是在 HTTP 规范的初始草案中由菲利普·霍尔姆(Philip Hallam-Baker)提出的,并且由于广泛采用而被固化下来,成为了官方标准(在 RFC 1945 和 2616 等中定义)。因此,无论是在代码还是协议中,都必须使用这个错误的拼写 Referer
Referer 头的主要作用
- 流量来源分析(Analytics)
这是最常见和最重要的用途。网站管理员和分析工具(如 Google Analytics)通过分析 Referer 头来了解:
- 用户从哪里来? 是来自搜索引擎、其他网站的链接(引荐流量)、社交媒体(如b站、Twitter),还是直接输入网址访问?
- 搜索引擎的关键词是什么? 从搜索结果的 URL 中可以提取出用户搜索的关键词,这对于 SEO 优化至关重要
- 广告活动效果如何? 通过在广告链接中添加特殊参数(UTM),并结合 Referer 分析,可以追踪不同广告渠道的转化效果
- 防盗链(Hotlink Protection)
很多网站(尤其是图片、视频、文件托管站点)使用 Referer 头来防止其他网站直接链接(盗链)自己的资源
-
原理:服务器在提供图片等资源时,会检查请求中的 Referer 值
- 如果 Referer 来自自己的域名(如 my-website.com),则允许访问并返回图片
- 如果 Referer 来自一个未经授权的第三方网站(如 other-site.com),服务器可以返回一个错误(如 403 Forbidden)或一张替代图片(如“禁止盗链”的提示图)
-
例子:你在自己的博客里直接引用了某图床网站上的图片链接。如果该图床开启了防盗链,你的博客读者可能就无法看到这张图片
- 优化缓存和安全策略
在某些情况下,Referer 可以作为上下文信息,帮助服务器决策如何提供内容或实施安全策略,尽管这并非其主要用途
Referer 头的隐私与安全问题
Referer 头在提供便利的同时,也带来了隐私和安全方面的顾虑:
-
信息泄露:
- 搜索关键词:如上面的例子所示,Referer 可能会将你在搜索引擎中查询的关键词泄露给目标网站和所有中间网络节点
- 敏感URL:如果你从一个包含敏感信息的页面(如内部管理系统、带有会话ID的URL)点击链接,这个敏感的 URL 可能会通过 Referer 泄露给第三方。虽然URL中的密码通常会被浏览器自动剥离,但其他信息仍可能泄露
-
安全风险:
- 在某些不安全的实现中,泄露的会话ID可能被用于会话劫持等攻击
控制与管理 Referer
出于隐私和安全的考虑,浏览器和标准组织提供了多种方式来控制 Referer 头的发送。
- Referrer-Policy(来源策略)
这是一个更新的 HTTP 响应头(也可在 HTML 的 标签中设置),允许网站开发者精细地控制浏览器如何发送 Referer 信息。常见的策略包括:
-
no-referrer:完全不发送 Referer 头
-
no-referrer-when-downgrade(默认策略):
- 从 HTTPS 页面链接到 HTTP 页面(安全降级)时不发送 Referer,以防止信息泄露到不安全的连接
- 在其他情况(如 HTTPS->HTTPS)下发送完整的 Referer
-
same-origin:仅当目标与来源同源(相同协议、域名、端口)时发送 Referer
-
origin:只发送来源的根路径(例如 https://example.com),而不发送完整的 URL(如 https://example.com/path?id=1),平衡了分析需求和隐私保护
-
strict-origin:类似 origin,但在从 HTTPS 降级到 HTTP 时不发送
-
strict-origin-when-cross-origin(推荐策略):
- 同源请求:发送完整的 Referer
- 跨域请求:只发送根路径
- 安全降级(HTTPS->HTTP):不发送
- 浏览器设置
用户可以在浏览器的高级设置中完全禁用 Referer 头的发送,但这可能会破坏一些网站的防盗链或分析功能,现在更推荐使用 Referrer-Policy
总结:Referer 头的要点
来用一个图总结一下
方面 | 描述 |
---|---|
是什么 | HTTP 请求头,指示当前请求的来源页面URL |
拼写 | 规范中是错误的拼写 Referer |
作用 | 1. 流量分析(了解用户来源、搜索词),2. 防盗链(保护网站资源),3. 上下文缓存与策略 |
隐私风险 | 可能泄露搜索关键词、会话ID等敏感信息 |
控制方法 | 1. Referrer-Policy HTTP头(服务器控制),2. 浏览器设置(用户控制) |
Referer头注入的前置条件
Referer头注入本质上是HTTP响应头注入的一种特定形式。要成功利用此漏洞,必须同时满足一系列条件,缺一不可。这些条件环环相扣,既涉及应用程序的功能设计,也涉及其安全缺陷
前置条件一:应用程序使用了Referer头的值
这是最根本的前提。如果应用程序完全忽略Referer头,那么攻击就无从谈起。具体来说,应用程序必须在服务器端的逻辑中读取并处理Referer头的值。常见的使用场景包括:
-
将其反射到HTTP响应头中:这是最经典的产生漏洞的场景
- 例如,设置一个自定义头:X-Forwarded-Host: <Referer值>
- 例如,用于控制缓存行为的头:Cache-Control: public, max-age=3600 (但其值的一部分来自Referer)
- 重定向:应用程序将Referer的值放入 Location 响应头中,用于跳转
- CORS:将Referer的值填入 Access-Control-Allow-Origin 头(错误配置时)
-
将其反射到HTTP响应体中(HTML页面):
- 例如,在页面中显示“您是从 <Referer值> 跳转过来的”。如果输出时未进行HTML编码,则可能引发XSS,但这属于另一种漏洞(反射型XSS),而非严格的“头注入”
-
用于服务器端的业务逻辑或安全判断:
- 例如,用于记录日志、分析流量来源
- 例如,用于实现蹩脚的CSRF防护(检查Referer是否来自自家域名)。在这种情况下,注入可能用于绕过检查(通过注入换行符和合法的域名来欺骗检查逻辑)。
如果应用程序完全不用Referer头,那么此漏洞不存在
前置条件二:应用程序对Referer头的处理存在安全隐患
仅仅使用Referer头还不够,必须是不安全地使用。关键的安全隐患在于:
-
缺乏有效的输入验证和过滤:这是漏洞产生的核心
- 应用程序没有过滤掉HTTP报文中不允许的字符,特别是换行符(Carriage Return %0d or \r 和 Line Feed %0a or \n)
- 在HTTP协议中,\r\n 标志着一个头部的结束或头部与消息体的分隔。如果攻击者可以在值中注入 \r\n ,他们就可以提前结束当前头部,并开始注入一个新的、恶意的头部。
-
进行了字符串拼接:应用程序通常会将不可信的用户输入(Referer)与固定的字符串进行拼接,然后赋值给一个响应头
- 例如:
$response->setHeader('X-Forwarded-Host', $_SERVER['HTTP_REFERER']);
- 这种简单的拼接是极度危险的
- 例如:
前置条件三:注入点必须在HTTP响应头之前被处理
要污染的是HTTP 响应头,因此这个不安全的行为必须发生在服务器开始发送响应体之前
- 这意味着漏洞代码通常位于生成HTTP状态行或头部的那部分服务器逻辑中
- 如果是在输出HTML页面时才使用Referer值,那么最多只能造成XSS,无法进行HTTP头注入
前置条件四:攻击者能够控制或欺骗Referer头的值
这似乎显而易见,但值得明确。攻击者必须有方法让目标应用程序接收到他精心构造的恶意 Referer 值。主要有两种方式:
-
直接控制:攻击者可以使用Burp Suite、Postman、cURL等工具,直接伪造一个HTTP请求,并手动设置 Referer头为任何他想要的值。
-
curl -H “Referer: 恶意payload” http://victim.com/page
-
-
间接欺骗:攻击者需要诱骗用户的行为来间接实现。例如:
- 他可以在自己控制的网站上放置一个链接或表单,该元素的目标URL是存在漏洞的 victim.com 页面。当用户点击时,浏览器会自动将当前恶意页面的URL(攻击者可以控制)作为Referer发送给victim.com
- 他可以利用一些浏览器或插件的漏洞来修改请求的Referer(较少见)
总结与关系图
这四个前置条件逻辑上是串联的,必须同时满足:
什么是 Referer 头注入
Referer 头注入是一种安全漏洞,攻击者通过向 HTTP Referer 头字段中注入恶意的、非预期的内容(通常是换行符),来操纵服务器的响应,从而实现其他更高级的攻击,如缓存投毒、Web缓存欺骗、绕过安全限制、SQL注入,甚至在某些情况下引发跨站脚本(XSS)
其本质是:应用程序(服务器端)不可信地使用了来自客户端(Referer头)的数据,并且没有进行严格的验证、过滤或编码就将其反映在输出(如响应头或响应体)中
本系列还是以 SQL注入 为主,Referer头其他类型的注入之后有机会再讲
什么是 Referer 头SQL注入
Referer头SQL注入 是一种攻击技术。攻击者通过篡改或伪造HTTP请求中的 Referer 头字段,在其中插入恶意的SQL代码。如果目标应用程序的后端代码存在安全漏洞,即未经验证和过滤就直接将 Referer 头的值拼接到了SQL查询语句中,那么这些恶意代码就会被数据库执行。
简单来说,它就是SQL注入,只不过攻击的入口从常见的“登录表单”、“搜索框”等,变成了不那么起眼的 Referer HTTP请求头
攻击原理与流程
其原理与所有SQL注入一致:“用户输入数据被当作代码执行”。关键在于,这里的“用户输入”是 Referer 头
就以本次靶场less-18的源代码举例,来看看到底为什么存在注入
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);function check_input($value){if(!empty($value)){// truncation (see comments)$value = substr($value,0,20);}// Stripslashes if magic quotes enabledif (get_magic_quotes_gpc()){$value = stripslashes($value);}// Quote if not a numberif (!ctype_digit($value)){$value = "'" . mysql_real_escape_string($value) . "'";}else{$value = intval($value);}return $value;}$uagent = $_SERVER['HTTP_REFERER'];$IP = $_SERVER['REMOTE_ADDR'];echo "<br>";echo 'Your IP ADDRESS is: ' .$IP;echo "<br>";//echo 'Your User Agent is: ' .$uagent;
// take the variables
if(isset($_POST['uname']) && isset($_POST['passwd'])){$uname = check_input($_POST['uname']);$passwd = check_input($_POST['passwd']);/*echo 'Your Your User name:'. $uname;echo "<br>";echo 'Your Password:'. $passwd;echo "<br>";echo 'Your User Agent String:'. $uagent;echo "<br>";echo 'Your User Agent String:'. $IP;*///logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a');fwrite($fp,'Referer:'.$uname."\n");fclose($fp);$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";$result1 = mysql_query($sql);$row1 = mysql_fetch_array($result1);if($row1){echo '<font color= "#FFFF00" font size = 3 >';$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";mysql_query($insert);//echo 'Your IP ADDRESS is: ' .$IP;echo "</font>";//echo "<br>";echo '<font color= "#0000ff" font size = 3 >'; echo 'Your Referer is: ' .$uagent;echo "</font>";echo "<br>";print_r(mysql_error()); echo "<br><br>";echo '<img src="../images/flag.jpg" />';echo "<br>";}else{echo '<font color= "#0000ff" font size="3">';//echo "Try again looser";print_r(mysql_error());echo "</br>"; echo "</br>";echo '<img src="../images/slap.jpg" />'; echo "</font>"; }}?>
最重要且起到关键作用的代码也就:
这段代码的意思是从HTTP请求头中获取Referer,将获取的数据执行下面的SQL:
因为提取数据后没有处理用户输入的内容,导致恶意输入被直接执行
没有看懂的可以看Web安全:深入理解User-Agent报头注入与防御,里面详细审计了这段代码(因为less-18和less-19的注入的原理一样,所以源码基本一样,这两个注入的原理和方式也一样,只不过换了个地方注入)
实战演练
环境设置: 本示例为 sqli-labs 19
工具准备: Burp Suite
less-19 和 less-18一样有安全绕过所以必须登录正确的用户名和密码,登陆成功后他才能把 Referer 插入到数据库中
输入正确的用户名和密码后开始抓包
这就是本篇的主角
将数据发送到 Repeater,不会的看:Web安全:深入理解User-Agent报头注入与防御
上一篇用的 updatexml报错注入 那这篇就换个花样,咱们用 extractValue报错注入
直接在 Referer 里构建注入语句,extractValue()报错注入不会的可以看:extractValue()报错注入
' or extractvalue(1,concat('~',(select database()))),2) #
查询表名也同理
' or extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database()))),2) #
查询表名,列名,只需要将 select database() 替换了即可,由于篇幅有限,这里就不详细写了,不清楚的可以看extractValue()报错注入里面有详细过程
防御之道:如何防御Referer头注入
-
核心原则:永远不要信任来自客户端的任何数据! 这包括所有HTTP头(Cookie, User-Agent, Referer, X-Forwarded-For等)
-
具体防御方案:
-
严格校验与过滤(针对输出):
- 如果必须将Referer输出到HTML页面,必须进行严格的HTML编码(HtmlEncode)。例如,PHP可用htmlspecialchars()函数,Java可用OWASP ESAPI等
-
白名单校验(针对输入):
- 如果业务逻辑必须使用Referer(如简易的防盗链),应使用白名单机制,只允许指定的域名或URL模式
-
避免使用Referer进行安全决策:
- 绝对不要用它来做CSRF防护。请使用CSRF Token等安全标准
- 不要用它做重要的权限校验
-
使用更安全的替代方案:
- 对于防盗链,可以使用签名URL或时间戳验证等更安全的方式
-
代码审计:
- 在代码中全局搜索HTTP_REFERER, request.Headers[“Referer”]等关键字,审查其使用场景是否安全
-