同源策略和跨域

时光怂恿深爱的人放手 提交于 2019-12-09 14:49:51

要掌握跨域,首先要知道为什么会有跨域这个问题出现

同源策略:协议、域名、端口都相同,是一种安全策略,不同源的客户端脚本在没有明确授权的情况下,不能读取对方资源。

 

同源策略的目的:

保证用户的信息安全,防止恶意的网站盗取数据。如果缺少了同源策略,浏览器很容易受到xss、csrf的攻击。

设置同源策略的主要目的是为了安全,如果没有同源策略,在浏览器中的cookie等其他数据可以任意读取,不同域下的DOM任意操作,ajax任意请求其他网站的隐私数据。

设想这样一个场景:一个恶意网站的页面通过iframe嵌入了银行的登陆页面(两者不同源)如果没有同源策略的限制,恶意网站上的js脚本就可以在用户登录银行的时候获取用户名和密码。

(1)做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。
(2)把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
(3)这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

 

跨域问题是由于浏览器为了防止CSRF攻击,避免恶意攻击带来的风险而采取的同源策略限制。CSRF攻击就是利用用户的登录状态发起已请求,但是跨域不能完全阻止CSRF,因为跨域是请求发出去了,但是被浏览器拦截了响应。

没有同源策略限制的两大危险场景

据我了解,浏览器是从两个方面去做这个同源策略的,一是针对接口的请求,二是针对Dom的查询。试想一下没有这样的限制上述两种动作有什么危险。

没有同源策略限制的接口请求

有一个小小的东西叫cookie大家应该知道,一般用来处理登录等场景,目的是让服务端知道谁发出的这次请求。如果你请求了接口进行登录,服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这个用户已经登录过了。知道这个之后,我们来看场景:
1.你准备去清空你的购物车,于是打开了买买买网站www.maimaimai.com,然后登录成功,一看,购物车东西这么少,不行,还得买多点。
2.你在看有什么东西买的过程中,你的好基友发给你一个链接www.nidongde.com,一脸yin笑地跟你说:“你懂的”,你毫不犹豫打开了。
3.你饶有兴致地浏览着www.nidongde.com,谁知这个网站暗地里做了些不可描述的事情!由于没有同源策略的限制,它向www.maimaimai.com发起了请求!聪明的你一定想到上面的话“服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中”,这样一来,这个不法网站就相当于登录了你的账号,可以为所欲为了!如果这不是一个买买买账号,而是你的银行账号,那……
这就是传说中的CSRF攻击浅谈CSRF攻击方式
看了这波CSRF攻击我在想,即使有了同源策略限制,但cookie是明文的,还不是一样能拿下来。于是我看了一些cookie相关的文章聊一聊 cookieCookie/Session的机制与安全,知道了服务端可以设置httpOnly,使得前端无法操作cookie,如果没有这样的设置,像XSS攻击就可以去获取到cookieWeb安全测试之XSS;设置secure,则保证在https的加密通信中传输以防截获。

没有同源策略限制的Dom查询

1.有一天你刚睡醒,收到一封邮件,说是你的银行账号有风险,赶紧点进www.yinghang.com改密码。你吓尿了,赶紧点进去,还是熟悉的银行登录界面,你果断输入你的账号密码,登录进去看看钱有没有少了。
2.睡眼朦胧的你没看清楚,平时访问的银行网站是www.yinhang.com,而现在访问的是www.yinghang.com,这个钓鱼网站做了什么呢?

// HTML
<iframe name="yinhang" src="www.yinhang.com"></iframe>
// JS
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你输入账号密码的Input')
console.log(`拿到了这个${node},我还拿不到你刚刚输入的账号密码吗`)

由此我们知道,同源策略确实能规避一些危险,不是说有了同源策略就安全,只是说同源策略是一种浏览器最基本的安全机制,毕竟能提高一点攻击的成本。其实没有刺不穿的盾,只是攻击的成本和攻击成功后获得的利益成不成正比。

 

同源策略限制下接口请求的正确打开方式

1.JSONP
在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,利用这一点,我们可以这样干:

 

2.空iframe加form
细心的朋友可能发现,JSONP只能发GET请求,因为本质上script加载资源就是GET,那么如果要发POST请求怎么办呢?

 

3.CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)跨域资源共享 CORS 详解。看名字就知道这是处理跨域问题的标准做法。CORS有两种请求,简单请求和非简单请求。

这里引用上面链接阮一峰老师的文章说明一下简单请求和非简单请求。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

1.简单请求
后端

// 处理成功失败返回格式的工具
const {successBody} = require('../utli')
class CrossDomain {
  static async cors (ctx) {
    const query = ctx.request.query
    // *时cookie不会在http请求中带上
    ctx.set('Access-Control-Allow-Origin', '*')
    ctx.cookies.set('tokenId', '2')
    ctx.body = successBody({msg: query.msg}, 'success')
  }
}
module.exports = CrossDomain

前端什么也不用干,就是正常发请求就可以,如果需要带cookie的话,前后端都要设置一下,下面那个非简单请求例子会看到。

fetch(`http://localhost:9871/api/cors?msg=helloCors`).then(res => {
  console.log(res)
})

2.非简单请求
非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!