跨域

作者: 椰果粒 | 来源:发表于2019-07-12 16:37 被阅读0次

    写在前面:
    跨域是面试经常会问到的问题。
    在工作中,开发环境可以用webpack-dev-server,线上环境服务端会配置好。

    为什么会出现跨域问题

    浏览器同源策略导致了跨域问题

    浏览器同源策略

    浏览器的同源策略

    [协议域名端口] 这三个完全相同,就表示同域。

    • 协议:http, https等
    • 域名:一级域名,二级域名等
    • 端口:80,8080等
    域名地址的组成

    跨域的几种情况,不同域之间进行资源请求就相当于跨域了。

    URL 描述 是否允许
    http://www.baidu.com/a.js
    http://www.baidu.com/a.js
    同一域名,协议,端口(80) 允许
    http://www.baidu.com/src/a.js
    http://www.baidu.com/script/a.js
    同一域名,不同文件夹 允许
    http://www.baidu.com:8888/a.js
    http://www.baidu.com:9999/a.js
    不同端口 不允许
    http://www.baidu.com/a.js
    http://10.42.66.88/a.js
    域名和域名对应的IP 不允许
    http://www.baidu.com/a.js
    http://script.baidu.com/a.js
    主域名相同,子域名不同 不允许
    http://www.baidu.com/a.js
    http://baidu.com/a.js
    同一域名,不同二级域名 不允许(cookie也不允许)
    http://www.baidu.com/a.js
    http://www.google.com/a.js
    不同域名 不允许
    http://www.baidu.com/a.js
    https://www.baidu.com/a.js
    不同协议 不允许

    为什么浏览器默认不支持跨域(危险性,XSS 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攻击方式](http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html)。
    

    看了这波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},我还拿不到你刚刚输入的账号密码吗`)
    

    同源策略能规避一些危害,但并不是说有了同源策略就完全安全了,只能说同源策略是浏览器最基本的安全机制。

    哪些不支持跨域

    • cookie,localStorage是同一域下的,不支持跨域
    • DOM元素也有同源策略的限制
      • 在页面中引入别人的页面
    • Ajax也不支持跨域
      • 支持的话,只要知道接口链接,就会被别人引用你的接口了。不安全。

    三个允许跨域的标签

    • <img src="">
    • <link href="">
    • <script src="">

    为什么要实现跨域呢

    前端与后端放在不同的服务器上,就得跨域

    如何实现跨域

    • jsonp(json with padding)
    • cors(纯后端实现跨域)
    • postMessage(两个页面通信)
    • document.domain(有一个一级域名和二级域名是同一域下的,子域和父域跨域)
    • window.name
    • location.hash
    • http-proxy(反向代理,Webpack)
    • nginx(配置Nginx)
    • WebSocket(消息通信,实现页面通信,没有跨域限制。)

    说明:

    1. 如果是协议和端口造成的跨域,前端是无法解决的。
    2. 在跨域问题上,是根据域名端口协议是否匹配来识别,而不会根据域名对应的IP地址来判断。

    请求跨域到底发出去请求了没有?

    事实上,即便是跨域了,浏览器请求能发送出去,服务器能接收到请求并返回正确的结果,只是这个结果被浏览器拦截了。

    跨域是为了阻止读取到另一个域名下的内容,Ajax可以获取响应,但浏览器认为这不安全,所以就把响应拦截住了。

    jsonp方式实现跨域

    1. jsonp的原理
      利用<script>标签没有跨域限制的原理,网页可以得到从其他来源动态产生的json数据。
      创建一个script标签,把文件引进来

    2. jsonp和Ajax的区别
      jsonp和Ajax都是客户端向服务器发送请求,从服务器获取数据。但是Ajax属于同源策略,jsonp属于非同源策略(可以跨域)。

    3. jsonp的缺点

      1. 只支持get请求,不支持post,delete,put请求
      2. 不安全,容易受到xss攻击
      3. 现在很多网站都不采用jsonp的方式了
    4. jsonp示例
      index.html请求百度的数据。

    <script>
      function show(data){
        console.log(data);
      }
    </script>
    <script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=有钱&cb=show"></script>  
    
    1. 封装jsonp
    function jsonp({url, params, cb}){
      return new Promise((resolve, reject) => {
        let script = document.createElement('script')
        window[cb] = function(data){
          resolve(data)
          document.body.removeChild(script)
        }
        params = {...params, cb}
        let arrs = [];
        for(let key in params){
          arrs.push(`${key}=${params[key]}`)
        }
        script.src = `${url}?${arrs.join("&")}`
        document.body.appendChild(script)
      })
    }
    
    jsonp({
      url: "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
      params: {
        wd: '花'
      },
      cb: "show"
    }).then(data => {
      console.log(data);
    })
    

    cors方式实现跨域

    cors是目前开发中最常用的跨域解决方案
    cors的安全性比较高,只要在服务器端做出修改,就可以实现跨域。

    服务端设置Access-Control-Allow-Origin可以开启CORS,这个属性表示哪些域名可以访问资源,如果写成"*",允许任何源都可以访问。

    res.setHeader("Access-Control-Allow-Origin", origin); // 设置哪个源可以访问
    res.setHeader("Access-Control-Allow-Headers", "name");// 设置携带哪个头访问
    res.setHeader("Access-Control-Allow-Methods", "PUT");// 设置哪个方法可以访问
    res.setHeader("Access-Control-Allow-Credentials", true); // 允许携带cookie
    res.setHeader("Access-Control-Max-Age", 6000);  // 预检的存活时间
    res.setHeader("Access-Control-Expose-Headers", "name"); // 允许前端获取哪个头
    

    参考

    https://segmentfault.com/a/1190000015597029

    https://juejin.im/post/5c23993de51d457b8c1f4ee1

    相关文章

      网友评论

          本文标题:跨域

          本文链接:https://www.haomeiwen.com/subject/lcdekctx.html