CORS 的 pre-flight 似乎没有意义。是笑话吗?

It seems the pre-flight for CORS doesn't make sense. Is it a joke?

根据来自 here 的样本:

If a request uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.

所以在下面的例子中,pre-flight是因为XMLContent-Type和自定义headerX-PINGOTHER:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';

function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); //<====
      invocation.setRequestHeader('Content-Type', 'application/xml'); //<====
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

但在 so-called pre-flight OPTIONS 请求(如下)中,服务器仅被通知 HTTP 方法和自定义 header。 没有人告诉服务器 XML Content-Type

从逻辑上讲,只要发送预检请求,就暗示 Content-Type不在3种形式中不需要预检。但是还有许多其他的可能性。 底线是,服务器应该能够知道发送预检请求的原因。

那么服务端如何合理地决定是否允许这个缺少拼图(content-type)的请求呢?

CORS 几乎总是安全的。引用自 the Fetch Standard,

For resources where data is protected through IP authentication or a firewall (unfortunately relatively common still), using the CORS protocol is unsafe. (This is the reason why the CORS protocol had to be invented.)

However, otherwise using the following header is safe:

Access-Control-Allow-Origin: *

Even if a resource exposes additional information based on cookie or HTTP authentication, using the above header will not reveal it. It will share the resource with APIs such as XMLHttpRequest, much like it is already shared with curl and wget.

Thus in other words, if a resource cannot be accessed from a random device connected to the web using curl and wget the aforementioned header is not to be included. If it can be accessed however, it is perfectly fine to do so.

因此,服务器无需了解请求的内容类型就可以知道如何响应 OPTIONS 请求。服务器只需要知道:被询问的 URL 是只能通过 IP-based 身份验证访问,还是在某些防火墙或 Intranet 后面?如果是,那么它应该拒绝 OPTIONS 请求。如果不是,即如果可以使用 curl、wget、telnet 或您最喜欢的 HTTP 库等工具通过 public 互联网访问资源,那么它应该允许 OPTIONS 请求。这样做只会为浏览器提供与其他工具相同的访问权限。

服务器可以在随后的 POST 请求进入时做出进一步的决定。例如,服务器可能想要拒绝 POST 错误的 Content-Type。但它总是想这样做。它不想只拒绝来自 CORS-respecting 个浏览器的 OPTIONS 请求;相反,它应该拒绝来自任何来源的 POST 请求。

(浏览器之所以特殊的原因如下。考虑一个遵循不良安全做法的内部网,并且 IP 地址在一定范围内的任何人都可以读取,即任何使用公司计算机的人。通常情况下,这不是问题:公司内部使用curl的人可以访问数据,而公司外部使用curl的人不能访问数据。但是,考虑到公司内部有人浏览某些恶意网站,https://evil.com/. If evil.com uses the XHR API to access http://intranet/secret-data, then the request will come from a privileged IP address, since it's the browser on the privileged user's computer doing the request. To prevent these kind of security leaks, the CORS protocol was invented, so that instead of doing a direct POST to http://intranet/secret-data, the browser first does an OPTIONS request, for which the whole of http://intranet可能只会说"no you can't access this",然后 POST 永远不会发生。)