前端知库

Referrer-Policy 是什么?默认会把哪些信息发给其他网站?

Referer 请求头告诉目标服务器请求是从哪个页面来的,Referrer-Policy 决定这个头里带多少信息。浏览器默认用 strict-origin-when-cross-origin:同源发完整地址,跨源只发源,从 HTTPS 降到 HTTP 不发。

参考答案

简答

用户在 https://shop.example/order?id=123&token=abc 页面点了一个外部链接,浏览器会在请求里带上 Referer 头,告诉对方“我是从哪个页面来的”。Referrer-Policy 就是用来控制这个头里带多少信息的。

之所以要控制,是因为地址里常常有隐私数据,比如订单号、搜索词、甚至令牌。完整地址发给第三方,就等于把这些数据泄露了出去。

现在浏览器的默认策略是 strict-origin-when-cross-origin:同源请求带完整地址,跨源请求只带源,比如 https://shop.example/,从 HTTPS 页面请求 HTTP 地址时不带。需要更严格可以设成 no-referrer 或 same-origin。设置方式有响应头、<meta name="referrer">、元素上的 referrerpolicy 属性,链接还能用 rel="noreferrer"。

要注意,unsafe-url 会把完整地址发给任何目标,包括 HTTP 网站,MDN 明确提醒要慎用。另外不要拿 Referer 做安全校验,它可能被策略去掉。

Referer 里会带什么

Referer 头的名字本身就是 referrer 的拼写错误,规范沿用了下来,而控制它的 Referrer-Policy 拼对了。按 MDN 的说明,Referer 最多包含源、路径和查询字符串,不会包含 #section 这样的片段,也不会包含 username:password 这类用户信息。

所以真正可能泄露的是路径和查询字符串。比如重置密码页面的地址带着令牌,页面上又引用了第三方统计脚本,要是 Referer 发了完整地址,第三方服务器的日志里就会记下这个令牌。

各个取值是什么意思

规范定义了八个取值,区别在于“同源还是跨源”和“安全等级有没有降低”。安全等级降低指从 HTTPS 页面请求 HTTP 地址。

  • no-referrer:任何请求都不发 Referer。
  • no-referrer-when-downgrade:安全等级不降低时发完整地址,降到 HTTP 时不发。
  • origin:任何请求都只发源,比如 https://example.com/。
  • origin-when-cross-origin:同源发完整地址,跨源和降级时只发源。
  • same-origin:同源发完整地址,跨源不发。
  • strict-origin:安全等级不变时只发源,降级时不发。
  • strict-origin-when-cross-origin:同源发完整地址;跨源且安全等级不变时只发源;降级时不发。
  • unsafe-url:任何请求都发完整地址,不管安全不安全。

默认值是 strict-origin-when-cross-origin。没有指定策略,或者指定的值无效时,都用它。MDN 提到,这是 2020 年 11 月规范修订后的默认值,以前的默认值是 no-referrer-when-downgrade,那时跨源请求也会带完整地址。

用 MDN 的例子看一下默认策略:

当前页面请求目标发出的 Referer
https://example.com/pagehttps://example.com/otherpagehttps://example.com/page
https://example.com/pagehttps://mozilla.orghttps://example.com/
https://example.com/pagehttp://example.com不发

第三行可能让人意外,明明是同一个域名,为什么不发?因为 http://example.com 和 https://example.com 协议不同,不是同源;而且从 HTTPS 到 HTTP 是降级,默认策略在降级时不发。

另外,W3C 规范规定,完整地址序列化后超过 4096 个字符时,浏览器会改为只发源。

怎样设置

最常用的是在 HTML 响应上加响应头,整个文档都按它来:

http
Referrer-Policy: strict-origin-when-cross-origin

响应头还能写多个值作为兜底,浏览器用最后一个它支持的值。下面这行在不支持 strict-origin-when-cross-origin 的旧浏览器里会退到 no-referrer:

http
Referrer-Policy: no-referrer, strict-origin-when-cross-origin

没法改响应头时,可以在页面里用 <meta> 设置整个文档的策略,也可以在单个元素上用 referrerpolicy 属性覆盖:

HTML
<meta name="referrer" content="no-referrer" />

<a href="https://partner.example" referrerpolicy="origin">合作方</a>
<img src="https://cdn.example/a.png" referrerpolicy="no-referrer" alt="" />

<a href="https://partner.example" rel="noreferrer">合作方</a>

referrerpolicy 属性可以用在 <a>、<area>、<img>、<iframe>、<script>、<link> 上,只能写一个值,多个值兜底只有响应头支持。rel="noreferrer" 可以用在 a、area、link 上。有个容易写错的地方:rel 里的 noreferrer 没有连字符,<meta> 里的 no-referrer 有连字符。

用 fetch 发请求时,可以通过 referrerPolicy 选项单独指定,取值和语义跟响应头一样:

JavaScript
fetch('https://api.partner.example/track', { referrerPolicy: 'no-referrer' });

CSS 里引用的资源也遵循策略。MDN 说明,外部样式表里的资源默认用 strict-origin-when-cross-origin,样式表自己的响应带了 Referrer-Policy 头就按那个;<style> 元素和 style 属性里的资源用所在文档的策略。

常见追问

能不能用 Referer 防 CSRF?

不能把它当唯一手段。Referer 会被 no-referrer 等策略去掉,服务器看到没有这个头时无法判断请求来源。防 CSRF 应该用 SameSite Cookie、CSRF 令牌这类机制,Referer 或 Origin 只能作为辅助检查。

设置成 no-referrer 有什么副作用?

同源请求也不发 Referer 了,依赖它的功能会受影响,比如统计站内的来源页面、某些按来源做防盗链的图片服务。如果只是想不泄露给第三方,same-origin 更合适,它在站内仍然发完整地址,跨源才不发。

为什么默认值从 no-referrer-when-downgrade 改了?

旧默认值只在降级到 HTTP 时不发,HTTPS 网站之间的跨源请求都会带完整地址,路径和查询字符串里的数据很容易泄露给第三方。新默认值跨源只发源,在保留“从哪个网站来”这个信息的同时,把路径和参数藏了起来。