Referer 是一个常见的请求头,在跳链或者发送请求时附带该头字段,可以让服务器识别请求发起的页面地址,以确定如何对请求进行响应,通常来说它有以下两个应用场景。
防止CSRF攻击
正常情况下,用户U在银行站点B完成转账,此过程中U的所有操作均发生在B的站点上,发送的请求携带B的Cookie。 现有一个攻击者站点A,用户U被诱导点进来之后A会自动向银行网站B发送转账请求(这里也可以携带B的Cookie),那么如果B网站没有做安全处理,转账请求可能就起作用了,结果用户U平白无故损失了一笔钱。 以上是一个简单的 CSRF 攻击场景,可见B站点需要补充一些安全措施来防御这种攻击。增加Referer头字段校验是其中一种方法,Referer 是浏览器自动添加的头字段,值一般是发送请求页面的origin(协议+域名),该字段无法人工添加和修改,B站点服务器可以通过检验该字段是否在配置的白名单之内,来限制那些伪造的请求。在本例中假设B的域名为www.money.com ,则从Referer头中提取出字符串,解析为URL格式,匹配域名部分是否相等即可。 那么这样做了之后,是不是就绝对安全呢?其实也不见得,问题就出在上句中标粗字体那里。随着业务的发展,B站点会把一些子域名、其他业务线的域名也纷纷加入白名单,然后有一天一位技术人员觉得白名单太长成了流水账,于是他把校验方式改成了正则校验/www\.money\.com/.test(referer) ,看上去白名单结构清晰多了,但却引入了意想不到的安全漏洞。 攻击者C某天浏览站点时发现转账的安全校验竟然如此简单,既没有token,也没有验证码,只通过一层Referer进行了限制。”窗户纸一捅就破!”C潜心研究了几个小时,发现链接http://c_site/www.money.com/a/b/c 竟然可以成功绕过Referer校验!是的,因为这个链接可以成功通过正则匹配,也就为C实施接下来的一系列恶意操作提供了便利。 你可能觉得上面的例子太简单,但这是根据一个真实的案例改编来的,IBM的邮箱系统都会出现这样的问题,安全无小事,还是要时刻注意提防。
统计用户特征
Referer头不仅可以携带请求方的origin,还可以通过修改配置让它携带完整的URL链接,这就为埋点统计提供了方便。比方说用户在https://www.money.com/a/b/c?query=d 的链接发送一个请求,Referer字段的值就是这个链接,后端服务器可以拿到该字段进行分析,来统计某个功能在特定页面使用的频次。 这种携带完整链接作为Referer的方式看似没啥问题,但这既方便了你,同时也方便了别人。那谁是“别人”呢?比如说你的站点被cdn攻击恶意植入了一个a标签,当用户点击这个标签之后,攻击者就可以通过Referer头字段推测出你站点的层级结构,更危险的情况是URL上携带有token等身份信息时,攻击者就可以盗取用户token进行攻击。另一种更糟糕情况是你的站点还存在Redirect导致的SSRF漏洞,攻击者盗取链接后可以进行分析并最终绕过安全认证,你的整个站点系统对他来说就一览无余了。 所以说Referer头携带完整URL链接也是不安全的,一直以来我都不建议在项目中这么去用,如果真的是要统计埋点数据的话,在前端构造请求即可。 上面提到a标签可能泄露你的站点链接,所以项目中包含跨域链接时最好做一些处理,如<a href="http://example.com" referrerpolicy="origin"> ,<a href="http://example.com" rel="noreferrer"> ,这两种方法都限制了Referer头部的发送,你也可以直接添加meta标签全局设置Referer发送策略<meta name="referrer" content="origin"> 上面提到的Referer控制机制叫做Referrer Policy ,浏览器提供了很多选项来控制请求中是否发送Referer头部,然后是发送origin还是完整链接,下面是几个常见的选项:
项目 | 含义 |
---|
no-referrer | 始终不携带Referer头部 | origin | 始终发送origin信息作为Referer | no-referrer-when-downgrade | 请求同协议的链接始终携带完整url,否则不发送(不推荐) | same-origin | 相同源站点发请求时携带完整url,否则发送origin |
完整的选项和示例介绍可以看这里。
|