浏览器何时发送 Origin 标头?浏览器何时将原点设置为空?
正如您从 这个 Bugzilla 线程(和 也),Firefox 没有始终在 POST 请求中发送 Origin 标头.RFC 声明不应以某些未定义的隐私"发送-敏感"上下文.Mozilla 在此处定义了这些上下文.
As you can see from this Bugzilla thread (and also), Firefox does not always send an Origin header in POST requests. The RFC states that it should not be sent in certain undefined "privacy-sensitive" contexts. Mozilla defines those contexts here.
我想知道这是否是 Firefox 不发送 Origin 标头的唯一情况.据我所知,它也不会在跨域 POST 请求中发送它(尽管 Chrome 和 Internet Explorer 会),但我无法在文档中确认这一点.是否列举了我遗漏的地方?
I'd like to know whether these are the only situations in which Firefox will not send the Origin header. As far as I can tell, it also will not send it in cross-origin POST requests (though Chrome and Internet Explorer will), but I can't confirm that in the documentation. Is it enumerated somewhere that I'm missing?
就相关规范的实际要求而言,答案有几个部分:
As far as what the relevant specs actually require, the answer has a couple parts:
- 当浏览器必须在内部将原点设置为将被序列化为
null
的值时 - 当浏览器必须发送 Origin 标头时
详情如下:
HTML 规范使用术语 opaque origin 并将其定义为内在价值":
The HTML spec uses the term opaque origin and defines it as an "internal value":
没有序列化,它可以从(它被序列化为空"每个原点的 ASCII 序列化)重新创建,唯一有意义的操作是测试是否相等
with no serialization it can be recreated from (it is serialized as "null" per ASCII serialization of an origin), for which the only meaningful operation is testing for equality
换句话说,HTML 规范在任何地方都说opaque origin,您可以将其转换为 null
.
In other words everywhere the HTML spec says opaque origin, you can translate that to null
.
HTML 规范要求浏览器在这些情况下设置不透明来源或唯一来源:
The HTML spec requires browsers to set an opaque origin or unique origin in these cases:
- 跨源图像(包括跨源
img代码>元素)
- 跨源媒体数据(包括跨源
video
和audio
元素) - 从
data:
URL 生成的任何文档一个> - 任何带有
沙箱的
iframe
不包含值allow-same-origin
的 code> 属性 - 使用
createDocument()
等以编程方式创建的任何文档. - 任何没有创建者浏览上下文的文档
- 网络错误响应
- 应该导航响应来自源中的导航请求类型目标被内容安全策略阻止? 算法在导航响应上执行时返回 Blocked
- Cross-origin images (including cross-origin
img
elements) - Cross-origin media data (including cross-origin
video
andaudio
elements) - Any document generated from a
data:
URL - Any
iframe
with asandbox
attribute that doesn’t contain the valueallow-same-origin
- Any document programmatically created using
createDocument()
, etc. - Any document that does not have a creator browsing context
- Responses that are network errors
- The Should navigation response to navigation request of type from source in target be blocked by Content Security Policy? algorithm returns Blocked when executed on a navigate response
在一种情况下,Fetch 规范要求浏览器将原点设置为全局唯一标识符"(这基本上与不透明原点"的含义相同,基本上意味着 null
...):>
The Fetch spec requires browsers to set the origin to a "globally unique identifier" (which basically means the same thing as "opaque origin" which basically means null
…) in one case:
URL 规范要求浏览器在以下情况下设置不透明来源:
The URL spec requires browsers to set an opaque origin in the following cases:
- For
blob:
URLs - For
file:
URLs - For any other URLs whose scheme is not one of
http
,https
,ftp
,ws
,wss
, orgopher
.
但请注意,仅仅因为浏览器在内部设置了一个不透明的原点——本质上是 null
——并不一定意味着浏览器会发送一个 Origin
标头.因此,有关浏览器何时必须发送 Origin
标头的详细信息,请参阅此答案的下一部分.
But note that just because the browser has internally set an opaque origin—essentially null
—that doesn’t necessarily mean the browser will send an Origin
header. So see the next part of this answer for details about when browsers must send the Origin
header.
浏览器为由 fetch()
或 XHR 调用或 JavaScript 库(axios、jQuery)中的 ajax 方法发起的跨域请求发送 Origin
标头等)——但不适用于普通的页面导航(即,当您直接在浏览器中打开网页时),也不适用于(通常)嵌入在网页中的资源(例如,不适用于 CSS 样式表、脚本、或图像).
Browsers send the Origin
header for cross-origin requests initiated by a fetch()
or XHR call, or by an ajax method from a JavaScript library (axios, jQuery, etc.) — but not for normal page navigations (that is, when you open a web page directly in a browser), and not (normally) for resources embedded in a web page (for example, not for CSS stylesheets, scripts, or images).
但这种描述是一种简化.当浏览器发送 Origin
标头时,除了跨源 XHR/fetch/ajax 调用,还有浏览器为嵌入资源发送 Origin
标头的情况.那么下面是更长的答案.
But that description is a simplification. There are cases other than cross-origin XHR/fetch/ajax calls when browsers send the Origin
header, and cases when browsers send the Origin
header for embedded resources. So what follows below is the longer answer.
在规范要求方面:规范要求 Origin
标头仅针对 Fetch 规范定义为 CORS 请求:
In terms of the spec requirements: The spec requires the Origin
header to be sent only for any request which the Fetch spec defines as a CORS request:
CORS 请求 是包含 Origin
标头的 HTTP 请求.它不能被可靠地识别为参与 CORS 协议,因为 Origin
标头 也包含在方法既不是 GET
也不是 HEAD的所有请求中代码>
.
A CORS request is an HTTP request that includes an
Origin
header. It cannot be reliably identified as participating in the CORS protocol as theOrigin
header is also included for all requests whose method is neitherGET
norHEAD
.
因此,规范的含义是:Origin
标头在所有跨域请求中发送,但它也始终为所有 POST、
PUT
、PATCH
和 DELETE
请求——即使对于 same-origin POST
、PUT
、PATCH
和 DELETE
请求(根据 Fetch 中的定义,它们实际上是CORS 请求"——即使它们是同源).*
So, what the spec means there is: The Origin
header is sent in all cross-origin requests, but it’s also always sent for all POST
, PUT
, PATCH
, and DELETE
requests — even for same-origin POST
, PUT
, PATCH
, and DELETE
requests (which by definition in Fetch are actually "CORS requests" — even though they’re same-origin).*
浏览器必须发送 Origin
标头的其他情况是使用CORS 标志"设置发出请求的任何情况——就 HTTP(S) 请求而言,navigate
、websocket
、same-origin
或 no-cors
.
The other cases when browsers must send the Origin
header are any cases where a request is made with the "CORS flag" set — which, as far as HTTP(S) requests, is except when the request mode is navigate
, websocket
, same-origin
, or no-cors
.
XHR always 将模式设置为 cors
.但是对于 Fetch API,您可以使用 fetch(...)
方法的 init-object 参数的 mode
字段设置这些请求模式:
XHR always sets the mode to cors
. But with the Fetch API, those request modes are the ones you can set with the mode
field of the init-object argument to the fetch(…)
method:
fetch("http://example.com", { mode: 'no-cors' }) // no Origin will be sent
字体请求总是将模式设置为 cors
,因此总是有 Origin
标头.
Font requests always have the mode set to cors
and so always have the Origin
header.
对于任何具有 crossorigin
属性的元素(又名CORS设置属性"),HTML规范要求浏览器将请求模式设置为cors
(并发送Origin
标题).
And for any element with a crossorigin
attribute (aka "CORS setting attribute"), the HTML spec requires browsers to set the request mode to cors
(and to send the Origin
header).
否则,对于嵌入式资源——任何具有发起请求的 URL 属性的元素(、样式表、图像、媒体元素)——请求的模式默认为
no-cors
;并且由于这些请求是 GET
请求,这意味着,根据规范,浏览器不会为它们发送 Origin
标头.
Otherwise, for embedded resources — any elements having attributes with URLs that initiate requests (<script src>
, stylesheets, images, media elements) — the mode for the requests defaults to no-cors
; and since those requests are GET
requests, that means, per-spec, browsers send no Origin
header for them.
当 HTML 表单元素发起 POST
请求时,那些 POST
的模式也默认为 no-cors
— 与嵌入资源的模式默认为 no-cors
.但是,与 no-cors
模式 GET
对嵌入式资源的请求不同,浏览器确实会为那些 no-corsOrigin
标头/code> 模式 POST
从 HTML 表单元素发起.
When HTML form elements initiate POST
requests, the mode for those POST
s also defaults to no-cors
— in the same way that embedded resources have their mode defaulted to no-cors
. However, unlike the no-cors
mode GET
requests for embedded resources, browsers do send the Origin
header for those no-cors
mode POST
s initiated from HTML form elements.
原因是,正如本答案前面提到的,浏览器总是在所有 POST
、PUT
、Origin
标头中发送 Origin
code>PATCH 和 DELETE
请求.
The reason for that is, as mentioned earlier in this answer, browsers always send the Origin
header in all POST
, PUT
, PATCH
, and DELETE
requests.
此外,为了完整和清晰:对于导航,浏览器不发送 Origin
标头.也就是说,如果用户直接导航到资源——通过将 URL 粘贴到浏览器地址栏中,或通过访问另一个网络文档中的链接——那么浏览器不会发送 Origin
标头.
Also, for completeness here and to be clear: For navigations, browsers send no Origin
header. That is, if a user navigates directly to a resource — by pasting a URL into a browser address bar, or by following a link from another web document — then browsers send no Origin
header.
*Fetch 规范中的算法 要求浏览器为所有 CORS 请求发送 Origin
标头是这样的:
* The algorithm in the Fetch spec that requires browsers to send the Origin
header for all CORS requests is this:
要附加请求 Origin
标头,给定请求 request,请运行以下步骤:
To append a request Origin
header, given a request request, run these steps:
1.让 serializedOrigin 成为使用 request 对请求源进行字节序列化的结果.
2.如果 request 的响应污染是cors
"或者 request 的模式是websocket
",然后
将 Origin
/serializedOrigin 附加到 request 的标头列表.
3.否则,如果 request 的方法既不是 GET
也不是 HEAD
,
然后:[在这种情况下也发送 Origin
标头]
1. Let serializedOrigin be the result of byte-serializing a request origin with request.
2. If request’s response tainting is "cors
" or request’s mode is "websocket
", then
append Origin
/serializedOrigin to request’s header list.
3. Otherwise, if request’s method is neither GET
nor HEAD
,
then: [also send the Origin
header in that case too]
第 2 步要求在所有跨域请求中发送 Origin
标头 - 因为所有跨域请求都将其响应污点设置为cors"
".
Step 2 there is what requires the Origin
header to be sent in all cross-origin requests — because all cross-origin requests have their response tainting set to "cors
".
但是第 3 步需要 Origin
标头也被发送给 same-origin POST
, PUT
、PATCH
和 DELETE
请求(根据 Fetch 中的定义,它们实际上是CORS 请求"——即使它们是同源的).
But step 3 there requires the Origin
header to also be sent for same-origin POST
, PUT
, PATCH
, and DELETE
requests (which by definition in Fetch are actually "CORS requests" — even though they’re same-origin).
由于 改变了这一点,以上描述了 Fetch 规范当前如何定义要求于 2016-12-09 符合规范.在那之前,要求是不同的:
The above describes how the Fetch spec currently defines the requirements, due to a change that was made to the spec on 2016-12-09. Up until then the requirements were different:
• 以前没有为同源 POST 发送 Origin
• 以前没有从 (没有 CORS)
• previously no Origin
was sent for a same-origin POST
• previously no Origin
was sent for cross-origin POST from a <form>
(without CORS)
因此,问题描述的 Firefox 行为是规范以前要求的内容,而不是当前要求的内容.
So the Firefox behavior the question describes is what the spec previously required, not what it currently requires.