web 跨域问题(SOP/CORS/CSRF)

同源策略 - same origin policy(SOP)

  • 概念:两个网页同源是指这两个网页的协议,域名,端口全部相同
举例来说,http://www.example.com/dir/page.html这个网址,协议是http,域名是www.example.com,端口是80,它的同源情况如下:

http://www.example.com/dir2/other.html    同源
http://example.com/dir/other.html         不同源(域名不同)
http://v2.www.example.com/dir/other.html  不同源(域名不同)
http://www.example.com:81/dir/other.html  不同源(端口不同)
  • 目的和作用:是为了保证用户信息的安全,防止恶意的网站窃取数据
同源策略主要对网页有如下限制:
1. Cookie、LocalStorage和IndexDB无法读取
2. DOM节点无法获得
3. AJAX请求不能发送

跨域解决方案

我们可以通过以下几种方法来实现跨域数据共享:

方案1:设置 document.domain

主域名相同的情况下,不同子域名的页面可以设置 document.domain 让它们同域,同域 document 提供的是页面间的互操作,需要载入 iframe 页面

// URL http://a.com/foo  访问http://b.a.com/bar页面的元素
var ifr = document.createElement('iframe');
ifr.src = 'http://b.a.com/bar'; 
ifr.onload = function(){
    var ifrdoc = ifr.contentDocument || ifr.contentWindow.document;
    ifrdoc.getElementsById("foo").innerHTML);
};

ifr.style.display = 'none';
document.body.appendChild(ifr);

// URL http://b.a.com/bar页面需要设置document.domain
document.domain = 'a.com'

方案2:具有src属性的HTML标签都是可以跨域的
在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以跨域加载资源,而不受同源策略的限制;
这些带"src"属性的标签每次加载时,实际上是由浏览器发起了一次GET请求;
不同于 XMLHttpRequest 的是,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容;
方案3:JSONP

JSONP 利用的是 script 标签可以跨域的特性,跨域 URL 返回的脚本不仅包含数据,还包含一个回调,只能用于 get 方法,下面是 http://a.com/foo 跨域访问 http://b.a.com/foo 的实现:

// URL: http://b.a.com/foo
var data = {
    foo: 'bar',
    bar: 'foo'
};
callback(data);

// URL: http://a.com/foo
var callback = function(data){
    // 处理跨域请求得到的数据
};
var script = $('<script>', {src: 'http://b.a.com/bar'});
$('body').append(script);
方案4:window.name

浏览器窗口有 window.name 属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。这种方法的优点是,window.name 容量很大,可以放置非常长的字符串,缺点是必须监听子窗口 window.name 属性的变化,影响网页性能

方案5:片段识别符

片段标识符(fragment identifier)指的是,URL的 # 号后面的部分,比如 http://example.com/x.html#fragment 的 #fragment。如果只是改变片段标识符,页面不会重新刷新

每次浏览器在当前窗口打开一个 URL 时,URL后面的 #xxx 部分会保留下来,那么新的页面可以从这里获得上一个页面的 ### 后面的内容

方案6:CORS-跨域资源共享(Cross-Origin Resource Sharing)
  • 概念

CORS 是 HTML5 标准中提出的,服务器通过设置 Access-Control-Allow-Origin 的响应头,如果 origin 请求头满足设置,浏览器将允许跨域,可以支持 POST,PUT 等方法,从根本上解决跨域问题。

实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉:

  • 简单请求和非简单请求

浏览器将 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
  • 简单请求的CORS处理

浏览器会在头信息中添加一个Origin字段,用来说明请求来自于哪个源

Host: ydcdn.nosdn.127.net
Origin: https://dashboard.youdata.com
Pragma: no-cache
Referer: https://ydcdn.nosdn.127.net/dash-devNetease/css/unit.f2a8cb0e41.css

不管是否跨域,服务器都会返回一个正常的http回应,但是如果允许跨域,服务器会设置一个 Access-Control-Allow-Origin 的头信息作为响应:

# 服务端允许哪些域跨域访问,*表示允许所有域名访问
Access-Control-Allow-Origin: http://api.bob.com 

# 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie
# 即使设置为 true,浏览器也需要设置 withCredentials=true,才会发送 cookie
Access-Control-Allow-Credentials: true  

# 在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
# 如果要访问其他头,则需要服务器设置本响应头,Access-Control-Expose-Headers 头是服务器把允许浏览器访问的头放入白名单
Access-Control-Expose-Headers: FooBar

如果是跨域请求,浏览器会自动判断当前的origin字段是否在 Access-Control-Allow-Origin 列表内,如果不在则会抛出异常,禁止浏览器访问服务器返回的数据,这个时候打开浏览器 console 控制台会看到如下类似报错:
在这里插入图片描述

这里需要注意的同源策略只是浏览器限制访问服务器的返回数据,但是仍然会向服务器发送请求。

  • 非简单请求的CORS处理

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json。

非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为"预检"请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错

# "预检"请求用的请求方法是OPTIONS
OPTIONS /cors HTTP/1.1

# 关键字段是Origin,表示请求来自哪个源
Origin: http://api.bob.com

# 该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Method: PUT

# 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段
Access-Control-Request-Headers: X-Custom-Header

Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器收到"预检"请求以后,做出如下回应:

# 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。
# 注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求
Access-Control-Allow-Methods: GET, POST, PUT

# 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。
# 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
Access-Control-Allow-Headers: X-Custom-Header

# 该字段与简单请求时的含义相同
Access-Control-Allow-Credentials: true

# 该字段可选,用来指定本次预检请求的有效期,单位为秒。,在此期间,不用发出另一条预检请求
Access-Control-Max-Age: 1728000

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段

CSRF 跨站攻击(Cross site request forgery)

CSRF(Cross site request forgery),即跨站请求伪造。我们知道 XSS 是跨站脚本攻击,就是在用户的浏览器中执行攻击者的脚本,来获得其 cookie 等信息。而 CSRF 是借用用户的身份,向 web server 发送请求,因为该请求不是用户本意,所以称为“跨站请求伪造”。一般存在 XSS 漏洞的网站,也极有可能存在 CSRF 漏洞。因为CSRF攻击中的那个“伪造的请求”的 URL 地址,一般是通过 XSS 攻击来注入到服务器中的。所以其实 CSRF 是以 XSS 为基础的,也可以看做是 XSS 攻击的一种。

CSRF一般的攻击过程是,攻击者向目标网站注入一个恶意的 CSRF 攻击 URL 地址(跨站url),当(登录)用户访问某特定网页时,如果用户点击了该 URL,那么攻击就触发了,我们可以在该恶意的 url 对应的网页中,利用 来向目标网站发生一个 get 请求,该请求会携带 cookie 信息,所以也就借用了用户的身份,也就是伪造了一个请求,该请求可以是目标网站中的用户有权限访问的任意请求。也可以使用 javascript 构造一个提交表单的 post 请求。比如构造一个转账的 post 请求。

SOP 同源策略解决的是浏览器不能访问服务器的返回结果,但是不能从根本上解决 CSRF 攻击,因为请求最终会被发送到服务器上,攻击者可能并不关心服务器的返回,更关心的是服务器上数据的更改,所有CSRF攻击的防御要从服务器端进行根本解决,一般从从以下几个方面进行防御:

  • referer

因为伪造的请求一般是从第三方网站发起的,所以第一个防御方法就是判断 referer 头,如果不是来自本网站的请求,就判定为 CSRF 攻击。但是该方法只能防御跨站的 csrf 攻击,不能防御同站的 csrf 攻击。

  • 使用验证码

每一个重要的 post 提交页面,使用一个验证码,因为第三方网站是无法获得验证码的,还有使用手机验证码,比如转账是使用的手机验证码。

  • 使用token

每一个网页包含一个 web server 产生的 token, 提交时也将该 token 放在请求头中提交到服务器,服务器对该 token 进行判断,如果 token 规则不对应,服务器就不作处理

SOP/CORS/CSRF 的区别:

  • 同源策略限制的是脚本发起的跨域请求,但仅仅是拦截响应,实际上请求已经发起了,并且不会限制对于通过html标签(< img>)、form 表单提交的跨域资源请求方式;
  • CORS 机制是从浏览器层面限制跨域访问,也就是浏览器会阻止网站访问跨域请求返回的数据,而 CSRF 攻击主要是对服务器数据的修改作为攻击目标,可能攻击者并不关心服务器的返回数据;
  • SOP 策略无法防止 CSRF 攻击,要通过服务器端从根本上解决跨域攻击;
参考文献
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值