当 Allow-Credentials 设置为 true 时,为什么我不能在 Access-Control-Allow-Headers 上使用通配符 *?

Why can't I use a wildcard * on Access-Control-Allow-Headers when Allow-Credentials is set to true?

我有一个网站,React 前端位于(比方说)app.mydomain.com 和 api 位于 api.mydomain.com。

用户向 API 提交登录请求,并在成功登录后收到一个不错的 cookie,以便稍后使用。 front-end 仅直接与 API 对话,因此 cookie 上的域只需设置为 api.mydomain.com.

我使用 Axios 执行 withCredentials 标志设置为 true 的请求,以便接收 cookie。

服务器上允许CORS的headers如下:

Access-Control-Allow-Origin: http://app.mydomain.com
Access-Control-Allow-Methods: GET,POST,DELETE,PUT,OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true

在这种情况下,这是来自 Firefox 的响应:

但是,一旦 Access-Control-Allow-Headers 值设置得更具体,就说 Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept,一切正常。

Mozilla says they do not allow "wildcarding" the Origin value, not the Headers one, same in this page,这里什么都没有提到。

为什么 Firefox 会这样,为什么在我能找到的任何地方都没有提到它?

在包含 Access-Control-Allow-Credentials: true 的预检响应中,用作 header Access-Control-Allow-Headers 值的星号按字面解释,而不是通配符(即“所有 headers 允许").

关于 Access-Control-Allow-Headers header 的主要 MDN page about CORS doesn't explicitly state this rule. However, the more specific MDN page 确实如此明确:

The value "*" only counts as a special wildcard value for requests without credentials (requests without HTTP cookies or HTTP authentication information). In requests with credentials, it is treated as the literal header name "*" without special semantics. Note that the Authorization header can't be wildcarded and always needs to be listed explicitly.

这里引用了更权威的 Fetch standard:

的澄清引述

For Access-Control-Expose-Headers, Access-Control-Allow-Methods, and Access-Control-Allow-Headers response headers, the value * counts as a wildcard for requests without credentials.

Access-Control-Expose-Headers, Access-Control-Allow-Methods, and Access-Control-Allow-Headers response headers can only use * as value when request’s credentials mode is not "include".

(我的重点)


规范规定的浏览器的相关规范要求在 main fetch 算法中 https://fetch.spec.whatwg.org/#main-fetch,在步骤 13,子步骤 2:

If request's credentials mode is not "include" and headerNames contains *, then set response’s CORS-exposed header-name list to all unique header names in response’s header list.

换句话说,规范声明要求 * 的浏览器行为是:

  • 如果凭据模式不是include”,浏览器需要检查响应中的实际响应列表header,并允许所有这些
  • 如果凭据模式include”,浏览器需要只允许任何响应header字面名称“[=13” =]"

在这一步的最后,实际上有一条注释说了几乎相同的事情:

One of the headerNames can still be * at this point, but will only match a header whose name is *.