朋友,你听说过跨域吗

大部分开发人员在前后端分离的场景下,都遇到过浏览器跨域问题,然后在网上搜了几个配置就解决了。但是,你真的了解跨域这件事吗?而你找到的配置又做什么?

看一下这几个问题,测试一下你对跨域的理解程度。

  1. 什么是跨域?和CORS是什么关系?
  2. 如果通过curl直接请求接口,并在header中增加origin,是否有跨域限制?
  3. 为什么有跨域限制?
  4. 什么时候会触发预检(PreFlight by OPTIONS)机制?
  5. 如何区分OPTIONS请求和预检(PreFlight)
  6. 跨域请求会携带cookie信息吗?

1. 什么是跨域?和CORS是什么关系?

CORS 是解决浏览器跨域限制的方案之一。

跨域: 简单来说是指浏览器在domain-a.com的域名下,异步加载非当前访问域名(e.g: domain-b.com)下的资源。如果协议+域名+端口号均相同,则是同域,否则,则是跨域。

cors_00

CORS:Cross-origin resource sharing (跨源资源共享) (或通俗地译为跨域资源共享)是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。

cors_01

2. 如果通过curl直接请求接口,并在header中增加origin,是否有跨域限制?

不会。跨域限制是浏览器行为。

服务端可以通过Request Header中的Origin判断是否跨域。如果服务端不做限制,浏览器的请求也会被执行,并返回结果。但是浏览器会解析Response中的header是否包含Access-Control-Allow-Origin且Value与当前同源,否则不会解析response body并抛出CORS error

cors_02

Origin在CORS或POST请求中才存在。

3. 为什么有跨域限制?

如果允许跨域,用户可能会遭到CSRF攻击。

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

cors_03

一个典型场景是,如果你在通过web浏览器访问email邮箱,点开了某个奇怪的链接,而这个链接则是自动向你的email邮箱服务器提交某个规则变更请求。如果没有跨域限制,则服务器判断为是你本人在登陆状态下发送的变更请求,而这个变更可能导致你的邮件全部被转发到攻击者邮箱内。

另一个经典场景则是iframe包裹真实网站,诱导用户进行敏感操作

4. 什么时候会触发预检(PreFlight by OPTIONS)机制?

预检请求先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求”的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

以Spring为例,如果预检请求不通过,则会直接返回403阻止后续继续发送实际请求。

cors_04

cors_05
cors_06

请求会区分简单请求和预检请求。非简单请求都会触发预检操作。

简单请求包括但不局限

  1. 使用下列方法之一:
    1. GET
    2. HEAD
    3. POST
  2. Content-Type 的值仅限于下列三者之一:
    1. text/plain
    2. multipart/form-data
    3. application/x-www-form-urlencoded

5. 如何区分OPTIONS请求和预检(PreFlight)

预检请求 会在header中增加Access-Control-Request-Method

比如跨域请求put请求,则会先发送一个 OPTIONS 请求,且header包括Access-Control-Request-Method=PUT

cors_07

cors_08

6. 跨域请求会携带cookie信息吗?

默认不会发送,可以通过设置withCredentials = true;让客户端发送cookie信息。
如果客户端设置withCredentials = true;但是服务端response未返回Access-Control-Allow-Credentials: true,则会抛出CORS error
cors_09
同时,为了避免误用导致的安全性问题,如果设置了allowCredentials,则不允许将Access-Control-Allow-Origin设置通配符*,只能设置具体的某个源。

Spring5.3版本提供了相关限制

cors_10