cros

what:什么是跨域?

  • 我有一个域名a.com和一个域名b.com
  • 我在a.com上有一个接口a.com/api,会返回一些数据
  • 我想在b.com域名下的一个页面上访问a.com/api得到数据
  • 浏览器阻止了我

why: 为什么不让我跨域?

  • 因为在web交互的环境中,只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

  • 跨站请求伪造(CSRF)

      1. 支付宝的转账操作是一个post请求,大概是https://alipay.com/api/withdraw/?to_user=XXX&amout=1000
      1. 我写了一段ajax的post请求代码,请求连接是上面的url。
      1. 然后我把这段代码嵌入我的网站a.com你不久前登陆过支付宝,
      1. 浏览器里保存了alipay.com域名的cookie
      1. 我让你访问a.com,打开页面,于是在你不知情的情况下发出了post请求,你的钱就被转到我的账号里了

how: 解决跨域问题:

  • 发请求前设置一下document.domain的值.
    • 父子域名
  • JSONP—json with padding.
    • 在非父子域关系的情况下,如developer.mozilla.org和production.mozilla.org(或者a.com和b.com),就是被浏览器当作两个不同的域名的,一般就会使用JSONP了.
  • 跨域资源共享(CORS).
    • 简单请求—GET和POST。
    • 预检请求—PUT和DELETE等,或者请求时添加了CORS安全的header之外的header(如自定义的)。
    • 带cookie的请求—要求响应头里Access-Control-Allow-Credentials为true,且Access-Control-Allow-Origin不能是通配符,防止后端犯错。

document.domain

  • 如果是子域名下的页面想访问父域的api,如developer.mozilla.org想访问mozilla.org的api,那可以在发请求前设置一下document.domain的值为mozilla.org。毕竟是子域,浏览器几乎没有做什么限制.

20201202185859

JSONP

  • JSONP 指的是 JSON with Padding
    • 从另一个域请求文件会引起问题,由于跨域政策,从另一个域请求外部脚本没有这个问题
1
2
➜ls
index.html    server.script
  • index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>

<body>

<h1>使用 script 标签请求 JSON</h1>
<p>PHP 文件所返回的函数调用处理的是 JSON 数据。</p>

<p id="demo"></p>

<script>
function myFunc(myObj) {
  document.getElementById("demo").innerHTML = myObj.city;
}
</script>

<script src="server.script"></script>

</body>
</html>
  • server.script
1
myFunc({ "name":"Bill Gates", "age":62, "city":"Seattle" })
JSONP缺陷
  • 因为作为一个script标签:
    • 一是浏览器只会使用GET方法去请求它
    • 二是请求它的时候不会携带cookie
    • 三是能被改造成JSONP形式的api一定是纯粹用来GET数据的.

一是因为JSONP是一种非标准行为,利用了script来做数据的事; 二是它使得别人能直接在他的网页上使用你的数据.

CORS:Cross-Origin Resource Sharing

简单请求

简单的GET和POST。

当developer.mozilla.org页面请求production.mozilla.org的api.

  • 浏览器发出请求时,request里会带上Origin头,值为developer.mozilla.org
  • 当api响应header里带的Access-Control-Allow-Origin字段包含(匹配)了发送请求的页面所在的域名(developer.mozilla.org),浏览器就会认为合法,把数据接着使用。
  • 否则,浏览器会拦截掉这段数据:没错,响应的数据已经放body里到达了客户端,而浏览器会阻止掉,让专栏页面里负责发ajax的那段js代码拿不到响应值。
    • 这样的好处很明显:我只需要在服务器端(网关比如nginx这一层)配置好Access-Control-Allow-Origin,从而使后端代码不需要写判断对请求域名进行处理(以前的确写过),就阻止其他人纯前端的手段这些后台数据, 完成了HTTP访问控制。
预检请求

PUT和DELETE等,或者请求时添加了CORS安全的header之外的header(如自定义的).

浏览器主动向目标api发出一个OPTIONS预检请求,这个请求里会带三个跨域相关的header,其值为预检之后,正式发送api请求时将会使用的

  • 来源/方法/请求头:
    • Origin
    • Access-Control-Request-Method
    • Access-Control-Request-Headers 预检请求的响应需要带着与它们对应匹配的header和值,这样浏览器才会去请求跨域api。

Same-origin policy

带cookie的请求

要求响应头里Access-Control-Allow-Credentials为true,且Access-Control-Allow-Origin不能是通配符,防止后端犯错。

点击劫持

点击劫持—clickjacking

实现原理可以如下:

  1. 假如支付宝有一个页面,页面上的按钮点击是转账1000元给XXX
  2. 我把这个页面作为一个iframe放在a.com的网页上
  3. 我把这个iframe设置为透明,在它的按钮位置下面放置一个可以看见的「下一页」按钮
  4. 你看见我的网页,毫无防备地点击了下一页,实际上点击的位置是转账按钮
  • 这种「跨域」也有类似CORS的控制方式,即X-Frame-Options响应头。它的值有三种:
    • DENY。表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
    • SAMEORIGIN。表示该页面可以在相同域名页面的 frame 中展示。
    • ALLOW-FROM uri。表示该页面可以在指定来源(uri)的 frame 中展示。

发现网页在iframe里,且X-Frame-Options响应头的值不符合要求,浏览器不会加载这个iframe。

附录

为什么给你设置重重障碍?讲一讲Web开发中的跨域