浏览器什么情况下发送Origin头信息?什么情况下浏览器将Origin设置为null?

85

如您从此Bugzilla线程(以及这个)所看到的,Firefox在POST请求中并不总是发送Origin头信息。RFC规定在某些未定义的“隐私敏感”情况下,不应发送。Mozilla在此处定义了这些情况。

我想知道是否只有在这些情况下Firefox才不会发送Origin头信息。据我所知,它还不会在跨域POST请求中发送它(尽管Chrome和Internet Explorer会),但我无法在文档中确认。是否有我遗漏的列出这些情况的地方?


这意味着为了防止攻击(尤其是CSRF),必须在后端处理它(即使使用CORS限制或同源策略),并禁用带有空原始标头的请求。 - Sahin
3个回答

130

就实际要求的相关规范而言,答案有几个部分:

  • 当浏览器必须将一个原点内部设置为一个被序列化为null的值时
  • 当浏览器必须发送Origin头部时

以下是详细信息:

当浏览器必须将原点设置为一个被序列化为null的值时

HTML规范使用术语opaque origin并将其定义为“内部值”:

没有序列化时,它可以从中重新创建(按照原点的ASCII序列化,它被序列化为"null"),唯一有意义的操作是测试相等性

换句话说,无论HTML规范在哪里提到opaque origin,您都可以将其翻译为null

HTML规范要求浏览器在以下情况下设置不透明原点或唯一原点:

  1. 跨域图像(包括跨域的img元素)
  2. 跨域媒体数据(包括跨域的videoaudio元素)
  3. data: URL生成的任何文档
  4. 任何带有sandbox属性且不包含值allow-same-originiframe
  5. 使用createDocument()等程序创建的任何文档
  6. 没有创建浏览上下文的任何文档
  7. 网络错误响应
  8. 当在导航响应上执行“Should navigation response to navigation request of type from source in target be blocked by Content Security Policy?”算法时,如果返回Blocked,则被阻止

根据Fetch规范,浏览器需要在以下情况下将origin设置为“全局唯一标识符”(基本上与“不透明起源”(即基本上意味着 null…)相同):

  1. 跨来源重定向

URL规范要求浏览器在以下情况下设置不透明起源:

  1. blob: URLs
  2. file: URLs
  3. 所有其方案不是httphttpsftpwswssgopher的其他URL

但请注意,仅因为浏览器在内部设置了一个不透明的来源(基本上是null),这并不一定意味着浏览器将发送一个Origin头。因此,请参阅本答案的下一部分,以了解浏览器必须发送Origin头的详细信息。


浏览器何时必须发送Origin头

浏览器在WebSocket请求和由fetch()或XHR调用发起的跨源请求中发送Origin头,或者由JavaScript库(axios、jQuery等)的ajax方法发起的跨源请求中发送Origin头,但不会在正常页面导航(即在浏览器中直接打开网页时)以及嵌入在网页中的资源(例如CSS样式表、脚本或图像)中发送(通常情况下)。

但这只是一个简化。除了WebSocket请求和跨源XHR/fetch/ajax调用之外,还有其他情况下浏览器会发送Origin头,并且在嵌入资源时也会发送Origin头。因此,下面是更详细的答案。


关于规范要求:规范要求在 WebSocket 请求和任何 Fetch 规范定义为 CORS 请求 的请求中,始终发送 Origin 标头:

CORS 请求 是包括 Origin 标头的 HTTP 请求。由于 Origin 标头“也包括所有方法不是 GETHEAD 的请求”,因此无法可靠地确定其是否参与 CORS 协议。

因此,规范的意思是:在所有跨域请求中都会发送 Origin 标头,但对于所有 POSTPUTPATCHDELETE 请求,甚至对于同源的 POSTPUTPATCHDELETE 请求(根据 Fetch 的定义,它们实际上是“CORS 请求”——尽管它们是同源的),也始终会发送该标头。*

浏览器必须发送Origin头的另一种情况是在设置了“CORS标志”的任何请求中,这在HTTP(S)请求方面是除非请求模式为navigatewebsocketsame-originno-cors

XHR 总是将模式设置为cors。但使用Fetch API时,这些请求模式是您可以使用init-object参数的mode字段设置到fetch(…)方法中的那些模式:

fetch("http://example.com", { mode: 'no-cors' }) // no Origin will be sent

字体请求始终将模式设置为cors,因此始终具有Origin标头。

对于任何带有{{link1:crossorigin属性}}(也称为“CORS设置属性”)的元素,HTML规范要求浏览器将请求模式设置为cors(并发送Origin标头)。

否则,对于嵌入资源 - 任何具有启动请求的URL属性的元素(<script src>、样式表、图像、媒体元素) - 请求的模式默认为no-cors;由于这些请求是GET请求,因此根据规范,浏览器不会为它们发送Origin标头。

当HTML表单元素发起POST请求时,这些POST的模式也默认为no-cors,就像嵌入资源的模式默认为no-cors一样。然而,与嵌入资源的GET请求不同的是,浏览器会发送Origin头信息给那些从HTML表单元素发起的no-cors模式的POST

原因是,正如本答案中早先提到的,浏览器总是在所有POSTPUTPATCHDELETE请求中发送Origin头信息。

此外,为了完整和清晰:对于导航,浏览器不发送Origin头信息。也就是说,如果用户直接导航到一个资源——通过将URL粘贴到地址栏中,或者通过从另一个Web文档中跟随链接——那么浏览器不会发送Origin头信息。


Fetch规范中的算法要求浏览器对所有CORS请求发送Origin头,该算法如下:

为请求request附加一个请求Origin头,请执行以下步骤:

1. 让serializedOrigin成为使用request进行字节序列化的请求来源的结果。
2. 如果request的响应污染是"cors"或request的模式是"websocket",那么
    将Origin/serializedOrigin附加到request的头部列表中。
3. 否则,如果request的方法既不是GET也不是HEAD
    那么:[在这种情况下也发送Origin头]

第二步要求在所有跨域请求中发送Origin头,因为所有跨域请求的响应污染设置为"cors"。

但是第三步还要求在同源的POSTPUTPATCHDELETE请求中也发送Origin头(根据Fetch的定义,它们实际上是“CORS请求”——尽管它们是同源的)。

以上描述了Fetch规范目前如何定义要求,这是由于2016年12月9日对规范进行的更改。在那之前,要求是不同的:

• 先前同源POST未发送Origin

• 先前未在没有CORS的情况下从<form>进行跨域POST发送Origin

因此,Firefox的行为是描述问题的规范先前所要求的,而不是当前所要求的。


1
你能否稍微概括一下:Header通常都会被包含,除非不需要。Get/Head请求考虑[Post],如果是由JS XHR发出的,则模式为CORS,然后头部也会被包含,但使用fetch时可以更改模式并且头部不会被包含。那么这是否意味着使用img标签进行get请求而不使用JS(fetch/xhr)将不会发送头部origin和referrer? - Muhammad Umer
1
你知道在ajax CORS请求中是否有任何方法可以将origin头设置为“null”,而无需创建一个沙盒iframe并使用postMessages进行通信吗?我希望进行匿名请求。 - Endless
这是那种在几句话中解释复杂主题的百万分之一评论。人性的希望重新获得了。 - flasher1101
@sideshowbarker,你的回答不知何故成为了有关Origin标头值为null时完整概述的唯一权威。甚至MDN文档都引用了你的回答。我一直在查看你提供的各种(过时的)链接,但除了问题中提到的那些来源外,我没有找到任何支持你论点的来源。你是如何发现例如Content-Security-Policy的情况的呢? - Advena
我对XHR总是“将模式设置为'cors'”的说法持怀疑态度。我确信你配置XHR对象的方式可以导致与“cors”、“no-cors”等概念相一致的行为,但这些概念更具体地适用于Fetch规范吧?XHR早于Fetch出现,所以它肯定有不同的底层实现(即使只是为了防止旧系统可能出现故障或其他意外后果)。这就使得从fetch规范中借鉴来确定在使用XHR发出的请求中何时包含某些头部(如Origin头部)变得更加棘手。 - Benjamin Curtis Drake
显示剩余4条评论

5
对我而言,这发生在向本地主机的相对URL进行超级标准的表单POST时,似乎是由于存在以下情况所引发的。
<meta name="referrer" content="no-referrer">

<head>中。 将其更改为:
<meta name="referrer" content="same-origin">

似乎让Firefox更加愉快。


简单快捷,谢谢。 - Mohammad Hassani
谢谢,这帮了我找到它。在我的情况下,是Referrer-Policy no-referrer头导致Origin未设置(随后由于CORS被拒绝请求)。 - Dario Seidl
但是这仅适用于“referer”标头,而不是OP所谈论的“origin”标头。 - Kernel James
@KernelJames 这就是问题所在——你本以为这只会影响 referer,但当设置为 no-referrer 时,它还会导致浏览器发送 Origin:null - andrewdotn

3

我想在@sideshowbarker的优秀回答中添加一些信息,这已成为该主题最全面的来源,甚至被MDN链接。为了补充这个答案,我查阅了各种规范,并确定了自回答发布以来发生的一些变化。我还组织了信息,使其更易于阅读。此外,我使用了2023年初的WebArchive引用,而不是直接链接,因为规范可能会在未来发生变化。

需要注意的是,@sideshowbarker和我的回答都应作为指南使用,因为浏览器维护者的实现、规则集和风格取决于他们的判断。

将它们用作指南,如果您不确定,请务必进行测试。


正如@sideshowbarker所提到的,各种规范将空的Origin头定义为:

  • 不透明的来源
  • 空来源

有了这个,我们可以通过规范来过滤出何时Origin头是null的:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接