HTTP的同源策略与跨域资源共享(CORS)机制
同源策略
准确的说,同源策略是指,浏览器内部在发起如下请求时,该来源必须是当前同源的HTTP资源:
1. 以跨站点的方式调用XMLHttpRequest或者Fetch API。
2. Web字体(用于CSS中@ font-face的跨域字体使用)
3. WebGL textures
4. 使用drawImage绘制到canvas的图像/视频帧。
5. 样式表(用于CSSOM访问)
注意:两个URI同源当且仅当它们的协议://host:port相同。
从第一点可以看到,浏览器限制从脚本内部发起跨域的HTTP请求——更准确的说,同源策略有的限制有两种表现:(1)限制发起AJAX请求(XMLHttpRequest,Fetch);(2)拦截其他跨站请求的返回结果;这取决于请求是否为简单请求。
CORS
跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种解决跨域请求的方案,其机制是使用一组额外响应头(Access-Control-Allow-Origin)和预检请求(OPTIONS)来使浏览器有权使用非同源资源。大部分的现代浏览器符合该标准。
简单请求
若请求满足所有下述条件,则该请求可视为“简单请求”:
使用下列方法之一:
GET
HEAD
POST
并且Content-Type的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
Fetch 规范定义了对 CORS 安全的首部字段集合,也就是说,不得手动设置除以下集合之外的字段(否则不为简单请求)。该集合为:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Downlink
Save-Data
Viewport-Width
Width
并且请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用XMLHttpRequest.upload 属性访问。
并且请求中没有使用 ReadableStream 对象。
简单请求会直接发送请求而不会触发预请求,但是不一定能拿到结果,这取决于请求的服务器Response的Access-Control-Allow-Origin内容。注意以上条件只要有一条不满足则不为简单请求。
简单请求跨域表现
发起请求服务http://127.0.0.1:8000/ajax.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AJAX</title>
</head>
<script>
function submitRequest() {var xhr = new XMLHttpRequest();xhr.open("GET", "http://127.0.0.1:8888/get", true);xhr.withCredentials = true;xhr.send();xhr.onreadystatechange = function(){if(xhr.readyState === 4 && xhr.status === 200){alert(xhr.responseText);}}
}
</script>
<button onclick="submitRequest()">AJAX</button>
</html>
非同源服务http://127.0.0.1:8888/:
from flask import Flask, request, render_template_string, sessionapp = Flask(__name__)
app.secret_key='random_secret_key'@app.route('/get', methods=['GET'])
def get():if session.get('user','')=='admin':return "Admin do something!"else:return "No Privilege..."@app.route('/login', methods=['GET'])
def login():user=request.args.get("user", "Null")session["user"]=usertemplate="""<h3> Login as {{ user }}... </h3>"""return render_template_string(template, user=user)if __name__ == '__main__':app.run(host='127.0.0.1', port=8888, debug=True)
发请求,可以看到请求确实已发送,并且可以带cookie(withCredentials),但是js没有拿到结果:
AJAX请求结果(请求成功,回传失败,所以这也是GET型CSRF无法很好防范的原因):
综上,对于简单跨域请求,若未正确配置则请求正常发送,不能获取返回结果(浏览器拦截)。
Origin和Access-Control-Allow-Origin
可以看到在请求中存在Origin字段,它标记了来源,对应的Access-Control-Allow-Origin为回应包头携带字段,它表示那些来源可以访问本域,*表示所有来源(注意它不能与credentials一起使用)。
使用CORS实现的支持跨域的非同源服务http://127.0.0.1:8888/:
@app.route('/get', methods=['GET'])
def get():if session.get('user','')=='admin':ret = "Admin do something!"else:ret = "No Privilege..."resp=make_response(ret)resp.headers['Access-Control-Allow-Origin'] = "http://127.0.0.1:8000"resp.headers['Access-Control-Allow-Credentials'] = 'true'resp.headers['Access-Control-Allow-Methods'] = "POST, GET, OPTIONS, PUT, DELETE, PATCH"return resp
其中还有几个header:
Access-Control-Allow-Credentials:如果请求需要带cookie,该header必须为true,同时Access-Control-Allow-Origin不能为*,否则同样拿不到结果;
Access-Control-Allow-Methods:允许的请求方式
Origin和Access-Control-Allow-Origin一个为请求携带的字段,一个为回应携带的字段,浏览器以此来判断js是否可以接收回应。
改造后前端终于能够拿到结果:
预检请求
若请求不为简单请求,那么在发起该请求前必须使用OPTIONS发送预验请求,服务器允许后才能发送实际请求(可以猜想这是为了防止CSRF)。
当请求满足一下任一条件时,该请求为非简单请求:
使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
人为设置了对 CORS 安全的首部字段集合 之外的其他首部字段。
Content-Type的值不属于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
请求中使用了ReadableStream对象。
预检请求跨域表现
假设有服务器http://127.0.0.1:8888/json:
@app.route('/json', methods=['GET','POST'])
def json():if request.method == 'GET':return render_template('json.html', Evil="Benign")else:if session.get('user','')=='admin':print("session:",session)data=request.jsonret='Admin do '+data["action"]else:ret="No Privilege2..."print(ret)return jsonify({'result': ret})
‘templates/json.html’内容为:
<html>
<title>{{ Evil }}</title>
<center>
<h1> Reset Password </h1>
<head>
<script type="text/javascript">
function submitRequest() {var xhr = new XMLHttpRequest();xhr.open("POST", "http://127.0.0.1:8888/json", true);xhr.setRequestHeader("Accept", "*/*");xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");xhr.withCredentials = true;xhr.send(JSON.stringify({"action":"change passwd..."}));xhr.onreadystatechange = function(){if(xhr.readyState === 4 && xhr.status === 200){alert(xhr.responseText);}}
}
</script>
</head>
<body>
<button onclick="submitRequest()">Conform</button>
</body>
</html>
同域不存在预检请求:
跨域出现OPTIONS请求,默认情况下跨域被阻止:
Access-Control-Request-Method:字段说明请求的操作。
允许跨域请求
在OPTIONS和POST报头加入Access-Control-Allow-Origin等字段
@app.route('/json', methods=['GET','POST','OPTIONS'])
def json():if request.method == 'GET':return render_template('json.html', Evil="Benign")else:if session.get('user','')=='admin':print("session:",session)data=request.jsonret='Admin do '+data["action"]else:ret="No Privilege2..."resp=make_response(jsonify({'result': ret}))resp.headers['Access-Control-Allow-Origin'] = "http://127.0.0.1:8000"resp.headers['Access-Control-Allow-Credentials'] = 'true'resp.headers['Access-Control-Allow-Methods'] = "POST, GET, OPTIONS, PUT, DELETE, PATCH"resp.headers['Access-Control-Allow-Headers'] = "origin, content-type, accept, x-requested-with"return resp
跨站成功,先发送OPTIONS,再发送POST,注意这两个报头必须都存在CORS字段。
与CORS有关的HTTP头
请求
Origin:<origin>:表示实际请求的源站
Access-Control-Request-Method: <method>:用于预检请求,表示真实的请求方法。
Access-Control-Request-Headers: <field-name>[, <field-name>]*:用于预检请求,表示真实请求所携带的首部字段(从抓包上来看chrome没有按要求来啊Orz)
响应
Access-Control-Allow-Origin: <origin> | *:允许外域URI
Access-Control-Allow-Credentials:false:是否允许浏览器读取response内容(如cookie)
Access-Control-Allow-Methods:用于预检请求响应,表示允许使用的HTTP方法
Access-Control-Allow-Headers:用于预检请求响应,表示允许携带的头部
Access-Control-Expose-Headers:允许响应时能获取的其他头部(在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头)
Access-Control-Max-Age:preflight请求的最大响应时间
HTTP的同源策略与跨域资源共享(CORS)机制相关推荐
- gorilla websocket无法跨域_聊聊浏览器同源策略与跨域方案详解
开发出高性能的 Web 应用固然重要,但安全问题也不容小觑.本文我们继续以 HTTP 为线索,展开来讲一讲浏览器安全相关的同源策略. 浏览器的同源策略(Same Origin Policy) 源(Or ...
- 同源策略和跨域解决方案
同源策略 一个源的定义 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源. 举个例子: 下表给出了相对http://a.xyz.com/dir/page.html同源检测的示 ...
- 同源策略、跨域以及跨域的三种解决方案详解
浏览器并非限制了http发起的请求,跨域请求可以正常发起,但是返回结果会被浏览器拦截. CORS的核心就在于 让服务器来确定是否允许跨域访问. 1.服务器代理: 2.cors跨域资源共享: 3.JSO ...
- 浏览器的同源策略及跨域解决方案
同源策略 一个源的定义 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源. 举个例子: 下表给出了相对http://a.xyz.com/dir/page.html同源检测的示 ...
- 浏览器的同源策略和跨域请求_学习版
目录 同源策略 : 跨域请求 : 跨域请求的常见解决方案 : 1. jsonp 2. cors(跨域资源共享) 3. proxy(代理) 同源策略 : 什么是同源策略 ? + 同源策略是 浏览器 ...
- 新蜂商城(newbee-mall-api)部分接口实验,跨域处理(同源策略,跨域访问,CORS),系统安全问题(Session,Cookie,Token,OAuth)(软件工程综合实践课程第十三周)
文章目录 一.要求 二.知识总结 跨域处理 1.同源策略 1.1 浏览器的同源策略 1.2 跨域请求实验 2 跨域访问 2.1 添加响应头来处理跨域 2.1.1 CORS 跨域资源共享 2.1.2 设 ...
- Django - - 进阶 - - 同源策略和跨域解决方案
目录 同源策略 一个源的定义 同源策略是什么 举个例子 jQuery中getJSON方法 JSONP应用 1, 同源策略 1.1 一个源的定义 如果两个页面的协议,端口(如果有指定)和域名都相同,则两 ...
- websocket中发生数据丢失_tcp协议;websocket协议;同源策略和跨域
tcp协议 为什么连接的时候是三次握手,关闭的时候却是四次握手? 答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文.其中ACK报文是用来应答的,SYN报 ...
- 服务器安全:浏览器同源策略与跨域请求、XSS攻击原理及防御策略、如何防御CSRF攻击
主要包括 浏览器同源策略与跨域请求 XSS攻击原理及防御策略 如何使用SpringSecurity防御CSRF攻击 CC/DDOS攻击与流量攻击 什么是SSL TLS HTTPS? 一.浏览器的同源策 ...
最新文章
- Linux之远程登录、远程拷贝命令 ssh scp
- Windows Phone开发(37):动画之ColorAnimation
- 5年前的Dubbo,2年前的Spring Cloud,都输给了这个架构!
- Redis中的set应用场景
- 微型计算机主存可以分为,计算机基础试题 (含答案)
- AutoCompleteTextView 和 TextWatcher 详解
- [转]【人是怎么废掉的?】
- 开课吧:什么是排序算法
- toj 2798 Farey Sequence
- 小马哥---深度解析mtk刷机平台报错解决 4032 8038等
- 网站备案相关问题解答
- 研究生学习生活日记——第五次组会
- 去除xp桌面图标阴影
- html链接ppt,PPT超链接怎么做?
- java编写websocket客户端
- 数学板块学习之FWT
- 前端经典面试题(60道前端面试题包含JS、CSS、React、网络、浏览器、程序题等)
- Win10-更改c盘下的用户文件夹名[转]
- RuoYi 若依平台登录密码忘记了-如何解决
- 微信 和 支付宝 公众服务号开发者文档
热门文章
- html5时间画布走动,javascript+HTML5 canvas绘制时钟功能示例
- MFC模块的动态链接库DLL以及静态链接库LIB编译后的调用
- Linux设备驱动之I/O端口与I/O内存
- IT兄弟连 JavaWeb教程 jQuery对AJAX的支持经典案例
- 学习JS的心路历程-函式(一)
- FineReport中如何实现自动滚屏效果
- 利用 UML 进行实体关系建模
- PHP数组传递给JavaScript以及json_encode的gbk中文乱码的解决
- 微软回顾3项安全计划成果 有效降低******
- 一起学习 网络规划设计师