浏览器同源策略、跨域问题及web安全

310 阅读6分钟

客户端向不同的源发送数据请求时,会发生跨域问题。

为什么会有跨域问题呢?

浏览器默认两个相同的源是可以相互操作DOM和访问资源的,两个不同的源之间相互操作DOM和访问资源会受到安全策略的制约,这就是同源策略。两个源的协议、域名和端口有任一个不同都属于非同源。

没有同源策略的限制不好吗?那样就不会有跨域问题了。

可以想象一下没有同源策略的制约的web世界是什么样子的。当你打开了一个购物网站,而又不小心打开了一个恶意站点,那么恶意站点就可以做如下事:

  • 修改你站点的DOM和CSSOM
  • 获取你站点的账号和密码
  • 获取你的Cookie

黑客获取了你的敏感数据后,就可以做一些恶意操作。所以,没有同源策略的web是不安全的。

同源策略制约了哪些行为呢?

  1. 当前域下的JS脚本不能操作其他域下的DOM。
  2. 当前域下的JS脚本不能访问其他域下的Cookie、localStorage、IndexDB等数据。
  3. 当前域下的ajax不能发起跨域请求。

同源策略保证了web的安全,但也制约了WEB的自由,有时我们确实又需要解除同源策略的限制,那如何解决跨域问题呢?

1、 JSONP

JSONP是利用script标签不受同源策略限制的特性来解决跨域问题的。

JSONP解决跨域问题有如下步骤:

  1. 声明回调函数,形参为服务器返回的数据,并把函数名作为参数传递给服务器。
  2. 创建script标签,script的src属性值为接口地址。
  3. 服务器接收到请求后,开始处理请求,把函数名和需要返回的数据拼接成字符串,返回到客户端。
  4. 客户端接收到服务端返回的数据后,执行声明的回调函数。
function jsonp(url,params,callback) {
    return new Promise((resolve, reject) => {
        let script = document.createElement('script');
        window[callback] = function(data){
            resolve(data)
        }
        params = {...params, callback};
        let searchParams = [];
        for(let key in params) {
            searchParams.push(`${key}=${params[key]}`)
        }
        script.src = `${url}?${searchParams.join('$')}`;
        document.body.appendChild(script)
    })
}

jsonp({
  url: localhost:8080,
  params: {name: 'red'},
  callback: 'show'
}).then(data => {
    console.log(data)
})

JSONP的局限性:

JSONP只能发送GET请求。

2、 CORS

CORS是通过后端实现的,后端通过设置Access-Control-Allow-Origin指定哪些域可以访问资源,也可以使用通配符*让所有站点都可以访问资源。

使用CORS解决跨域问题,发送的请求分两种情况,分别为简单请求复杂请求

简单请求有如下条件:

  1. 请求方式为GETPOSTHEAD
  2. Content-type为下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

通过CORS发送的请求不是简单请求,那么它就是复杂请求了。复杂请求在正式请求之前会发送一个请求方式为OPTIONS预检请求。预检请求的请求头带有origin字段,表示请求的来源,服务端根据这个字段判断是否允许请求。服务器返回的响应头中有Access-Control-Allow-Origin,则说明允许请求。

发送预检请求是为了确认本次请求是否在服务器允许请求的范围内,如果在,就发送正式请求。浏览器默认发送跨域请求时是不带Cookie的,如果要带Cookie,则需要设置withCredentialstrue

3、postMessage

用于两个不能窗体之间的通信。

4、nginx反向代理

跨域访问不同源的资源给WEB世界带来了自由,但同时也会WEB带来了安全隐患,如XSS攻击和CSRF攻击。

XSS攻击

什么是XSS攻击呢?

XSS跨站脚本攻击,是指黑客把恶意JS脚本注入到用户的页面上,把用户的敏感数据提交到恶意服务器上的攻击手段。

XSS攻击有哪些类型呢?

1、 存储型XSS攻击

攻击者把恶意脚本存储到网站服务器上,当用户打开含有恶意脚本的页面时,JS恶意脚本就会被加载并执行,把用户的敏感数据提交到恶意服务器。

2、 反射型XSS攻击

当用户点击含有恶意代码的URL时,服务器把URL上的恶意代码提取出来,返回给浏览器,浏览器加载并执行恶意代码,把用户的敏感信息提交到恶意服务器上。

3、 基于DOM的XSS攻击

这中攻击方式不会经过服务器,攻击者利用各种手段把恶意代码注入到页面中,如利用网络劫持。

如何预防XSS攻击:

  1. 为cookie加上Http-only,这样cookie就只能应用了发送HTTP请求,而不能被javascript访问。
  2. 利用CSP

CSP有两个作用:

  1. 限制加载其他的域下的资源文件
  2. 禁止向其他域提交资源文件

浏览器是允许加载第三方资源文件的,如果第三方资源文件含有恶意代码,那么就会遭到XSS攻击,CSP禁止向第三方域提交资源文件,这样就可以预防XSS攻击。

  1. 服务端对输入的脚本进行转码或过滤。

存储型XSS攻击是利用往用户页面中注入恶意JS脚本,把用户的关键数据提交到恶意服务器的攻击手段。那么在服务端对输如的JS脚本进行转码或过滤,那么恶意脚本就不会在浏览器上执行。

CSRF攻击

CSRF是跨站伪造攻击,CSRF攻击利用用户的登录信息和第三方站点实施攻击的手段。

实施CSRF攻击需要满足三个条件:

  1. 用户登录过目标站点,并且在浏览器保持有该站点的登录状态。
  2. 目标站点存在CSRF漏洞。
  3. 用户打开第三方站点

CSRF攻击有如下类型:

  1. get类型CSRF攻击
  2. POST类型CSRF攻击
  3. 链接类型CSRF攻击

有人可能会问,不是有同源策略制约吗,第三方站点怎么能向其他站点发送请求呢。其实,同源策略限制的是XMLHttpRequest,像表单请求或能造成CSRF攻击的GET请求,如<img src="http://csrf.com">是不受同源策略的限制的。

如何防止CSRF攻击呢?

1、使用sameSite

CSRF攻击利用的是用户在目标站点的登录状态,而用户的登录状态是由Cookie维持的,所以只要不把Cookie发送到第三方站点不就可以了吗。sameSite就是做这个事情的,它有三个值,分别为strict/lat/none

strict:想同站点发送请求时,会携带Cookie;向第三方站点发送请求时,不会携带Cookie。

lat: 发送GET请求时会携带Cookie。

none: 任何请求都不会携带Cookie。

所以,可以在Cookie中加入sameSite=strict来避免CSRF攻击。

2、验证请求源

通过origin字段验证请求源,如果请求源不是被允许请求的源,则服务端不对请求作出响应。

3、CSRF Token

当浏览器第一发送请求时,服务端返回响应数据,并将CSRF Token发送给浏览器嵌入到页面中,当再次发送请求时,就带上这个CSRF Token,如果没有这个CSRF Token,则服务端不对请求作出响应。