如果两个URL的协议、域名和端口相同,从代码层

时间:2019-10-03 11:42来源:美高梅手机游戏网站
本篇提供几个例子,从代码层面说明同源政策何时起作用,并尝试通过几种不同的方法解决跨域问题。 什么是同源策略  本文实例讲述了JavaScript同源策略和跨域访问。分享给大家供大

本篇提供几个例子,从代码层面说明同源政策何时起作用,并尝试通过几种不同的方法解决跨域问题。

  1. 什么是同源策略 

本文实例讲述了JavaScript同源策略和跨域访问。分享给大家供大家参考,具体如下:

先是第一版,一个简单的flask程序。自己请求自己,不会有什么问题。打开浏览器输入:http://127.0.0.1:4000 会返回字符串回去,交给浏览器,其解析运行其中的javascript代码,发出ajax请求,至同服务下的/get_data路由,得到数据后渲染至页面。代码如下:

    理解跨域首先必须要了解同源策略。同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。

1. 什么是同源策略

# -*- coding: utf-8 -*-from flask import Flaskapp = Flaskcontent = '''<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title></head><body><script>var xhr = new XMLHttpRequest();// 指定通信过程中状态改变时的回调函数xhr.onreadystatechange = function(){ // 通信成功时,状态值为4 if (xhr.readyState === 4){ if (xhr.status === 200){ document.body.innerHTML = xhr.responseText } else { document.body.innerHTML = xhr.statusText } }};xhr.onerror = function  { console.error(xhr.statusText);};// open方式用于指定HTTP动词、请求的网址、是否异步xhr.open('GET', 'http://127.0.0.1:4000/get_data', true);// 发送HTTP请求xhr.send;</script></body></html>'''@app.routedef hello(): return content@app.route('/get_data')def get_data(): return 'got data!'if __name__ == '__main__': app.run(port=4000)

    何谓同源:

理解跨域首先必须要了解同源策略。同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。

结果为:

        URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

何谓同源:

美高梅手机游戏网站 1image

   同源策略:

URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

此处需要注意的是,若127.0.0.1换成localhost则该请求会被同源政策禁止。

        浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。 (白帽子讲web安全[1])

同源策略:

此时,我们再启一个flask项目,端口定在5000,修改上面代码中xhr open的地址,使4000端口的ajax去5000取数据。

        从一个域上加载的脚本不允许访问另外一个域的文档属性。

浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。 (白帽子讲web安全[1])

# -*- coding: utf-8 -*-from flask import Flaskapp = Flask@app.route("/get_data_5000")def hello(): return "got data in 5000!"if __name__ == '__main__': app.run(port=5000)

    举个例子:

从一个域上加载的脚本不允许访问另外一个域的文档属性。

得到如下结果:

        比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码。

举个例子:

美高梅手机游戏网站 2image

    在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以加载跨域资源,而不受同源限制,但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码。

显然,由于端口号已经不一致,违反了同源政策,请求失败。

    另外同源策略只对网页的HTML文档做了限制,对加载的其他静态资源如javascript、css、图片等仍然认为属于同源。

在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以加载跨域资源,而不受同源限制,但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

在应用层,使用第三方包增加http response header的方式来处理。5000项目变为:

手机美高梅游戏网址,    代码示例(和由于端口不同而不同源):

另外同源策略只对网页的HTML文档做了限制,对加载的其他静态资源如javascript、css、图片等仍然认为属于同源。

# -*- coding: utf-8 -*-from flask import Flaskfrom flask_cors import CORSapp = FlaskCORS
http://localhost:8080/test.html 
    <html> 
      <head><title>test same origin policy</title></head> 
      <body> 
        <iframe id="test" src="http://localhost:8081/test2.html"></iframe> 
        <script type="text/javascript"> 
          document.getElementById("test").contentDocument.body.innerHTML = "write somthing"; 
        </script> 
      </body> 
    </html> 
http://localhost:8081/test2.html 
    <html> 
      <head><title>test same origin policy</title></head> 
      <body> 
        Testing. 
      </body> 
    </html> 

代码示例(

美高梅手机游戏网站,重启项目后,访问http://127.0.0.1:4000即可拿到5000的数据。

    在Firefox中会得到如下错误

美高梅手机游戏网站 3image

        Error: Permission denied to access property 'body'

<html>
  <head><title>test same origin policy</title></head>
  <body>
    <iframe id="test" src="http://localhost:8081/test2.html"></iframe>
    <script type="text/javascript">
      document.getElementById("test").contentDocument.body.innerHTML = "write somthing";
    </script>
  </body>
</html>

可以看到第三方包自动处理了response header。

    Document对象的domain属性存放着装载文档的服务器的主机名,可以设置它。
    例如来自"blog.csdn.net"和来自"bbs.csdn.net"的页面,都将document.domain设置为"csdn.net",则来自两个子域名的脚本即可相互访问。
    出于安全的考虑,不能设置为其他主domain,比如不能设置为sina.com

美高梅手机游戏网站 4image

  1. Ajax跨域
<html>
  <head><title>test same origin policy</title></head>
  <body>
    Testing.
  </body>
</html>

此处不只是flask,不只是python,各个语言,各种web框架,都可以在这一层通过第三方包的形式来处理跨域问题,如果找不到相关插件,大不了自己写一个好喽。

    Ajax (XMLHttpRequest)请求受到同源策略的限制。

在Firefox中会得到如下错误:

此时要Nginx上场了。思路是,启项目于4000和5000端口,浏览器不直接通过端口访问web服务,而是通过Nginx的80端口,Nginx将实际上有两个项目这件事情对浏览器屏蔽掉。改Nginx配置为:

    Ajax通过XMLHttpRequest能够与远程的服务器进行信息交互,另外XMLHttpRequest是一个纯粹的Javascript对象,这样的交互过程,是在后台进行的,用户不易察觉。

Error: Permission denied to access property 'body'

server { listen 80; server_name localhost; location / { proxy_pass http://127.0.0.1:4000; } location /get_data_5000 { proxy_pass http://127.0.0.1:5000/get_data_5000; } }

    因此,XMLHTTP实际上已经突破了原有的Javascript的安全限制。

Document对象的domain属性存放着装载文档的服务器的主机名,可以设置它。

然后去掉flask-cors插件,再将4000中的ajax url改为http://127.0.0.1/get_data_5000

    举个例子:

例如来自"blog.jb51.net"和来自"bbs.jb51.net"的页面,都将document.domain设置为"jb51.net",则来自两个子域名的脚本即可相互访问。

效果如图:

        假设某网站引用了其它站点的javascript,这个站点被compromise并在javascript中加入获取用户输入并通过ajax提交给其他站点,这样就可以源源不断收集信息。

出于安全的考虑,不能设置为其他主domain,比如//www.jb51.net/不能设置为sina.com

美高梅手机游戏网站 5image

        或者某网站因为存在漏洞导致XSS注入了javascript脚本,这个脚本就可以通过ajax获取用户信息并通过ajax提交给其他站点,这样就可以源源不断收集信息。

2. Ajax跨域

可以看到,此时不再需要response header中的Access-Control-Allow-Origin即可请求到另一个项目的服务。

   如果我们又想利用XMLHTTP的无刷新异步交互能力,又不愿意公然突破Javascript的安全策略,可以选择的方案就是给XMLHTTP加上严格的同源限制。

Ajax (XMLHttpRequest)请求受到同源策略的限制。

这种处理方法可以联想到设计模式中的facade模式,此时Nginx即为外观层,后面到底有多少个项目在提供服务,请求者不需要关心。

   这样的安全策略,很类似于Applet的安全策略。IFrame的限制还仅仅是不能访问跨域HTMLDOM中的数据,而XMLHTTP则根本上限制了跨域请求的提交。(实际上下面提到了CORS已经放宽了限制)

Ajax通过XMLHttpRequest能够与远程的服务器进行信息交互,另外XMLHttpRequest是一个纯粹的Javascript对象,这样的交互过程,是在后台进行的,用户不易察觉。

不过这种方式是有一定局限性的,即只有这个Nginx可导向的服务才可不受跨域问题影响。

   随着Ajax技术和网络服务的发展,对跨域的要求也越来越强烈。下面介绍Ajax的跨域技术。   

因此,XMLHTTP实际上已经突破了原有的Javascript的安全限制。

移除之前的Nginx配置,通过4000端口访问页面,将5000端口的项目通过Nginx访问,并在Nginx这一层,来处理response header。

2.1 JSONP

举个例子:

在这里抄一下配置。

    JSONP技术实际和Ajax没有关系。我们知道<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。因此在文档中可以调用/访问脚本中的数据和函数。如果javascript脚本中的数据是动态生成的,那么只要在文档中动态创建<script>标签就可以实现和服务端的数据交互。

假设某网站引用了其它站点的javascript,这个站点被compromise并在javascript中加入获取用户输入并通过ajax提交给其他站点,这样就可以源源不断收集信息。

location /get_data_5000 { proxy_pass http://127.0.0.1:5000/get_data_5000; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # # Custom headers and headers various browsers *should* be OK with but aren't # add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; # # Tell client that this pre-flight info is valid for 20 days # add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } if ($request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; } }

    JSONP就是利用<script>标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。其中callback函数本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。

或者某网站因为存在漏洞导致XSS注入了javascript脚本,这个脚本就可以通过ajax获取用户信息并通过ajax提交给其他站点,这样就可以源源不断收集信息。

配置中可写if,可按需要操作header,结果如下:

    第一个站点的测试页面(): 

如果我们又想利用XMLHTTP的无刷新异步交互能力,又不愿意公然突破Javascript的安全策略,可以选择的方案就是给XMLHTTP加上严格的同源限制。

美高梅手机游戏网站 6image

<script src="http://localhost:8081/test_data.js"> 
  <script> 
    function test_handler(data) { 
      console.log(data); 
    } 
</script> 

这样的安全策略,很类似于Applet的安全策略。IFrame的限制还仅仅是不能访问跨域HTMLDOM中的数据,而XMLHTTP则根本上限制了跨域请求的提交。(实际上下面提到了CORS已经放宽了限制)

OK, Well done!

    服务器端的Javascript脚本():

随着Ajax技术和网络服务的发展,对跨域的要求也越来越强烈。下面介绍Ajax的跨域技术。

test_handler('{"data": "something"}');

2.1 JSONP

    为了动态实现JSONP请求,可以使用Javascript动态插入<script>标签:

JSONP技术实际和Ajax没有关系。我们知道<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。因此在文档中可以调用/访问脚本中的数据和函数。如果javascript脚本中的数据是动态生成的,那么只要在文档中动态创建<script>标签就可以实现和服务端的数据交互。

<script type="text/javascript"> 
    // this shows dynamic script insertion 
    var script = document.createElement('script'); 
    script.setAttribute('src', url); 
    // load the script 
    document.getElementsByTagName('head')[0].appendChild(script);  
</script> 

JSONP就是利用<script>标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。其中callback函数本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。

        JSONP协议封装了上述步骤,jQuery中统一是现在AJAX中(其中data type为JSONP):
   

第一个站点的测试页面(

    为了支持JSONP协议,服务器端必须提供特别的支持[2],另外JSONP只支持GET请求。

<script src="http://localhost:8081/test_data.js">
  <script>
    function test_handler(data) {
      console.log(data);
    }
</script>

2.2 Proxy

服务器端的Javascript脚本(

    使用代理方式跨域更加直接,因为SOP的限制是浏览器实现的。如果请求不是从浏览器发起的,就不存在跨域问题了。

test_handler('{"data": "something"}');

编辑:美高梅手机游戏网站 本文来源:如果两个URL的协议、域名和端口相同,从代码层

关键词:

  • 上一篇:没有了
  • 下一篇:没有了