Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

跨域 #14

Open
Qhappyman opened this issue Dec 3, 2019 · 0 comments
Open

跨域 #14

Qhappyman opened this issue Dec 3, 2019 · 0 comments

Comments

@Qhappyman
Copy link
Owner

同源策略

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

最初,它的含义是指,A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。所谓“同源”指的是“三个相同”:

  • 协议相同

  • 域名相同

  • 端口相同

    同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

    设想这样一种情况:A 网站是一家银行,用户登录以后,A 网站在用户的机器上设置了一个 Cookie,包含了一些隐私信息(比如存款总额)。用户离开 A 网站以后,又去访问 B 网站,如果没有同源限制,B 网站可以读取 A 网站的 Cookie,那么隐私信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。

非同源主要由三种行为受限制

  1. 无法获取非同源网页的 cookie、localstorage 和 indexedDB。
  2. 无法访问非同源网页的 DOM (iframe)。
  3. 无法向非同源地址发送 AJAX 请求 或 fetch 请求(可以发送,但浏览器拒绝接受响应)。

什么是跨域

URL1 : http://kangbiao.org/index URL2 : https://kangbiao.org/index

是否跨域 : 是 原因 : 协议不同

URL1 : http://kangbiao.org/index URL2 :http://kangbiao.org:8080/index

是否跨域 : 是 原因 : 端口号不同

URL1 : http://kangbiao.org/index URL2 :https://kangbiao.org/index

是否跨域 : 是 原因 : 主机不同

URL1 : http://kangbiao.org/index URL2 : http://t1.kangbiao.org/index

是否跨域 : 是 原因 : 主机不同

通过上面的比较可以归纳出,跨域是指协议、主机地址、端口号这三个条件只要有一个不同则认为是跨域。

另一个疑问

请求跨域了,那么请求有没有发出去?

跨域并不是请求发不出去,请求能发出去,服务器端能收到请求并返回正常结果,只是被浏览器拦截了

为什么表单可以发起跨域请求,而ajax不可以?

说白了,表单就是提交数据,并不会获取最新的内容,所以可以发起跨域请求。而ajax可以获取响应,浏览器认为这并不安全,所以拦截了响应。

为什么要有跨域限制

Ajax 的同源策略主要是为了防止 CSRF(跨站请求伪造) 攻击,如果没有 AJAX 同源策略,相当危险,我们发起的每一次 HTTP 请求都会带上请求地址对应的 cookie,那么可以做如下攻击:

  1. 用户登录了自己的银行页面 mybank.commybank.com向用户的cookie中添加用户标识
  2. 用户浏览了恶意页面 evil.com。执行了页面中的恶意AJAX请求代码。
  3. evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
  5. 而且由于Ajax在后台执行,用户无法感知这一过程。

当我们浏览信息时,看起来获取到了所需要的内容,但是如果有恶意攻击我们是无法用肉眼察觉的,这些攻击会盗取个人信息,隐私

  1. DOM同源策略也一样,如果 iframe 之间可以跨域访问,可以这样攻击:

    做一个假网站,里面用iframe嵌套一个银行网站 mybank.com

  2. 把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。

  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

解决跨域

CORS

CORS 是一个 W3C 标准,全称是跨域资源共享(Cross-origin resource sharing),它允许浏览器向跨源服务器,发出XMLHttpRequest请求。

CORS跨域判定流程

  1. 浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。
  2. 服务器收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含 Access-Control-Allow-origin 字段,若配置过域名,则返回 Access-Control-Allow-origin + 对应配置规则里的域名的方式
  3. 浏览器根据接受到的 响应头里的 Access-Control-Allow-origin 字段做匹配,若无该字段,说明不允许跨域,从而抛出一个错误;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器接受该响应;若不同源,则说明该域名不可跨域,浏览器不接受该响应,并抛出一个错误。

服务器是否指定跨域请求

  1. 服务器允许跨域请求,但是 Origin 指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,被 XMLHttpRequest的onerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。

    <!--控制台返回结果-->
     XMLHttpRequest cannot load http://localhost/city.json.
     The 'Access-Control-Allow-Origin' header has a value 'http://segmentfault.com' that is not equal to the supplied origin. 
     Origin 'http://www.zhihu.com' is therefore notallowed access.
    
  2. 服务器不允许任何跨域请求

    <!--控制台返回结果-->
    XMLHttpRequest cannot load http://localhost/city.json.
    No 'Access-Control-Allow-Origin' header is present on the requested resource. 
    Origin 'http://www.zhihu.com' is therefore not allowed access.
    

    简单请求

    条件:

    1. 使用 GET、POST、HEAD 其中一种请求方法。

    2. Accept

    Accept-Language

    Content-Language

    Last-Event-ID

    Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

    1. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器

    2. XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。 请求中没有使用 ReadableStream 对象。

    对于简单请求,浏览器直接发起 CORS 请求,具体来说就是服务器端会根据请求头信息中的 origin 字段(包括了协议 + 域名 + 端口),来决定是否同意这次请求。

    如果 origin 指定的源在许可范围内,服务器返回的响应,会多出几个头信息字段:

    Access-Control-Allow-Origin: http://xxx.xxx.com
    Access-Control-Allow-Credentials: true
    Access-Control-Expose-Headers: FooBar
    Content-Type: text/html; charset=utf-8
    

    非简单请求

    请求方法是 put 或 delete,或者 content-type 的类型是 application/json

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

    预请求头部 :

    OPTIONS /cors HTTP/1.1
    Origin: http://api.bob.com
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: X-Custom-Header
    Host: api.alice.com
    Accept-Language: en-US
    Connection: keep-alive
    User-Agent: Mozilla/5.0...
    

    一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样了

    JSONP

    JSONP 的原理就是利用 <script> 标签的 src 属性没有跨域的限制,通过指向一个需要访问的地址,由服务端返回一个预先定义好的 Javascript 函数的调用,并且将服务器数据以该函数参数的形式传递过来,此方法需要前后端配合完成。 (只限于get请求)

    //定义获取数据的回调方法
    function getData(data) {
      console.log(data);
    }
    
    // 创建一个script标签,并且告诉后端回调函数名叫 getData
    var body = document.getElementsByTagName('body')[0];
    var script = document.gerElement('script');
    script.type = 'text/javasctipt';
    script.src = 'demo.js?callback=getData';
    body.appendChild(script);
    
    //script 加载完毕之后从页面中删除,否则每次点击生成许多script标签
    script.onload = function () {
      document.body.removeChild(script);
    }
    

    Nginx服务器代理

    浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。

    实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant