产品目录
1. bug当晚情形2. 试著查案3. 布吕马简述3.1 何为草同域(相混)?3.2 何为布吕马(布吕马是不是出现?)3.3 布吕马有甚么安全可靠难题?3.4 何为应用程序相混思路(same-origin policy)3.5 何为CORS(布吕马统计数据共享)?3.6 “单纯”和“繁杂”布吕马允诺心灵心路历程4. 归纳提及预览历史记录有关该文所推荐1. bug当晚情形
当晚三套控制系统,集成同一sizes登入。当中两个控制系统重定向到除此之外两个控制系统时应用程序会创下三次。
讶异关上F12,难题就无法Cadours。
2. 试著查案
关上控制面板难题就化解了?啊怪异!可能将是控制面板关上后,动态文档在应用程序端无须内存导致的。
关上F12明令禁止控制面板Network –> Disable cache增设,居然难题能Cadours,后端js的允诺的确是内存的。
推断三次创下其原因:后端jsservice ticket,再次登入后,再度允诺职权统计数据USB(第三次允诺创下),网页获得成功展现。
笔者对应用程序的行为不熟,这里只是猜测。
笔者控制系统sizes登入实现的CASUSB,所以应用session过期或者失service ticket票据。
应用程序创下三次fiddler抓包如图:
第一次异步允诺后,由于没有职权,302重定向访问sizes登入服务。这里控制面板会提示布吕马允诺,布吕马在布吕马详解部分详细介绍。
后端明确说了,不是后端的难题,化解不了。
笔者公司的后端就是硬气。
在后台处理,后台是springboot项目,增加配置:
spring: resources: cache: cachecontrol: max-age: 0 复制代码后端文档不会内存,难题化解。
禁用内存后,不会出现地址栏创下三次现象,fiddler抓包如图:
难题化解了,笔者对那个布吕马的报错产生了兴趣。之前也看过不少布吕马的该文,始终对布吕马云里雾里。春节找出收藏的布吕马该文,好好研读了一下,有所获,赶紧借此文分享出来。
3. 布吕马简述
做web开发,工作中肯定接触过如下应用程序控制面板报错:
No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
允诺的布吕马资源response的header中没有Access-Control-Allow-Origin信息。
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at example.com/
应用程序相混思路明令禁止读取布吕马资源。
Access to fetch at ‘example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.
在域http://localhost:3000下访问域https://example.com资源被明令禁止。
了解后面的内容后,就会明白这些报错的真正含义,也就能处理布吕马难题了。
3.1 何为草同域(相混)?
相同协议(http、https)相同主机名/ip(127.0.0.1、localhost、google.com)相同端口(80、443、8080)满足三个条件是相混,否则就是不同的域。
注意:localhost主机名虽然在网络层最终会解析为127.0.0.1,但是对于应用程序相混思路来说,localhost和127.0.0.1是不同的主机名,二者不同则为布吕马。
3.2 何为布吕马(布吕马是不是出现?)
1989诞生的World Wide Web最初的html只有纯文本。
世界上第两个web网页,只包含纯文本和超链接。
1993年引入<img>标签,在html渲染时允许加载图片资源,这样纯文本中就可以展现图片。像这样在html中允许加载子资源(subresource)的tag还有:
<ifame><link><form><audio><video><script>诚如上面html标签语义,所谓的“子资源(subresource)”就是例如表单、文档、音视频、脚本等外部资源。
当两个域中包含有上面tag的html渲染时,就会加载subresource,当这个subresource和当前域不相混时,布吕马允诺就出现了。例如,在两个域中xmlhttprequest(ajax)允诺除此之外两个域的USB时就是布吕马允诺。
3.3 布吕马有甚么安全可靠难题?
两个域中加载另两个域的文档、音视频、脚本等subresource时,大部分情形不会产生甚么安全可靠难题,但是有一些情形如果不做限制,就存在安全可靠隐患。
例如两个域中提供了基于cookie/session权鉴的发送邮件USB,该域允cookie后任意调用发送邮件USB攻击网站。
可能将有同学疑问:发送邮件USB如果需要权鉴认证才能获得成功调用,别人没有认证信息如何能获得成功调用呢?
实际上用户在应用程序端完成登入后,用户信息就存储在应用程序端(cookie),这时关上恶意网站就有可能将被恶意脚本携带用户信息完成攻击。
3.4 何为应用程序相混思路(same-origin policy)
既然布吕马允诺有安全可靠的难题,应用程序端就做了有关限制,称之为“应用程序相混思路”。
相混思路阻止读取布吕马允诺得到的资源。
这是广义的两个定义,实际上应用程序针对不同subresource有不同的限制思路,下面有做详细说明。
同源思路在1995年网景应用程序2.02中引入,最开始是为了保护布吕马DOM而设计的。
布吕马允诺有三种形式:
布吕马写(Cross-origin writes)布吕马内嵌(Cross-origin embeds)跨域读(Cross-origin reads)相混思路的规则定义如下:
<ifame>:布吕马内嵌允许(需要合适的X-Frame-Options)。<link>:布吕马内嵌允许(需要合适的Content-Type)。<form>:布吕马写允许。<audio>:布吕马内嵌允许。<video>:布吕马内嵌允许。<script>:布吕马内嵌允许,某些api的调用可能将会被明令禁止(例如ajax布吕马调用)。<img>:布吕马内嵌允许,通过JavaScript布吕马读或者在<canvas>中加载被明令禁止。3.5 何为CORS(布吕马统计数据共享)?
应用程序的相混思路能化解很多安全可靠的难题,但是其限制也带来了不便。
CORS(Cross-origin resource sharing)布吕马统计数据共享就是来放宽应用程序相混思路的严格限制,便于某些场景的使用。
草同域允诺,如图:
布吕马允诺,如图:
图中涉及到preflight允诺下面简述。
3.6 “单纯”和“繁杂”布吕马允诺心灵心路历程
这里重点讲述ajax布吕马允诺(使用应用程序内置fetch()函数)时,其允诺过程和化解办法。
两个域中ajax布吕马允诺另两个域的USB时,该允诺的心灵心路历程是由顾客端和被允诺资源服务端共同决定的。顾客端的行为是应用程序相混思路指定的,被允诺资源服务端行为由资源提供者具体实现提供。具体来说:
所谓的允诺“心灵心路历程”是指:该允诺从应用程序发起,到服务端响应,再到应用程序读取响应结果并展现这个过程。
如果是“单纯”的ajax布吕马允诺,那么应用程序会放行该允诺,如果服务端没有包含Access-Control-Allow-Origin的header信息,则应用程序会限制对允诺到资源reponse的读取。
如果是“繁杂”的ajax布吕马允诺,那么应用程序会先自行触发两个preflight允诺,根据服务端的相应header信息决定是否放行顾客端允诺。
这里所谓的“单纯”和“繁杂”允诺是有关规范定义的,两个“繁杂”允诺要至少满足如下当中两个条件:
非GET、POST或者HEAD允诺。允诺头信息包含除Accept、Accept-Language或者Content-Language外的头信息。允诺Content-Type的值不是application/x-www-form-urlencoded、 multipart/form-data或者text/plain。2.中说的头信息不包括应用程序自动给允诺加入的header信息,例如origin。
接下来用Crystal启动httpUSB服务,看看不同布吕马允诺的心灵心路历程:
Crystal安装参考官方文档,脚本basic_greet.cr为:
require “kemal”port =4000 get “/” do “Hello world!” end get “/greet” do “Hey!” end post “/greet” do |env| name = env.params.json[“name”].as(String) “Hello, #{name}!” end post “/greet_str” do |env| name = env.params “Hello, 获得成功了!” endKemal.config.port = port Kemal.run 复制代码使用命令sudo crystal run src/basic_greet.cr启动USB服务。
0). 草同域下允诺
从http://172.22.27.215:4000/greetUSB域下发送“单纯”的ajax允诺,如图:
草同域下允诺,一切正常,USB能发起获得成功并且应用程序能读取响应USB统计数据。
不同应用程序控制面板实现方式不大相同(但实现的规范是一样的),这里以FireFox应用程序为测试应用程序。
1). “单纯”的post布吕马允诺
从天涯bbs论坛域下发送“单纯”的ajax允诺,如图:
USB能发起获得成功。但是,如上图控制面板报错,应用程序相混思路禁止读取远端资源,提示
CORS header ‘
Access-Control-Allow-Origin’ missing ,也就是说响应头信息中缺少Access-Control-Allow-Origin信息。这里之所以是“单纯”允诺,是因为Content-Type是text/plain,参考上面“繁杂”允诺规则,不满足任意两个。
这里所以找两个http服务,是因为CrystalUSB是http的,如果在https域下调用,应用程序会直接明令禁止https域下允诺http资源。
2). “繁杂”的post布吕马写入
从天涯bbs论坛域下发送“繁杂”的ajax允诺。
控制面板报错如图:
网络抓包如图:
图中1.为preflight允诺,允诺方法为OPTIONS。服务端目前没有实现OPTIONS方法实现,提示404 Not Found。
图中2.为真正的POST允诺,因为1.的preflight允诺没有获得相混思路规定的头信息,所以2.的真正POST允诺被应用程序级别blocked。
注意图中2.的OPTIONS允诺是应用程序发起的,应用程序会带上一些header信息,比如:origin、Access-Control-Reqest-Method和Access-Control-Reqest-Headers。
这种情形下的允诺心灵心路历程为:先行的preflight允诺404 Not Found(“身先死”),真正的POST允诺没有发起获得成功(“出师未捷”)。也就是所谓的:“出师未捷身先死”。
那,“繁杂”的布吕马允诺preflight要求怎样的实现呢,才能满足应用程序CORS协议的要求呢?
应用程序在发送preflight后会寻找响应中的2个header:
Access-Control-Allow-Methods:CORS协议允许的允诺方法,例如GET、POST等。Access-Control-Allow-Headers:CORS协议允许的允诺header,例如Content-Type等。针对“繁杂”允诺的心灵心路历程来说,上面2个header必须匹配顾客端实际允诺信息,否则顾客端实际允诺可能将会被应用程序级别blocked。说白了,服务端允许发送甚么样方法的允诺、甚么样的头信息,顾客端才能获得成功发送。
preflight的响应信息还可以返回2个header,说顾客端某些信息:
Access-Control-Max-Age:增设preflight允诺能内存的秒数(默认值是5)。超过增设时间,“繁杂”允诺发起时应用程序会再次发起preflight;在增设时间内,无须发起preflight允诺(使用内存的preflight允诺)。Access-Control-Allow-Credentials:增设顾客端实际允诺是否能携带用户信息(例如cookie)。如果服务端没有返回上面2个header信息,不影响请求的心灵心路历程。
也就是说,根据CORS协议,preflight允诺响应头信息中要明确返回顾客端实际允诺的方法(通过响应头信息Access-Control-Allow-Methods值)和头信息(通过响应头信息Access-Control-Allow-Headers值),这样应用程序才会同意发送顾客端实际允诺。而preflight允诺响应头Access-Control-Max-Age可以指定preflight允诺内存的时间,默认就是5秒钟;preflight响应头Access-Control-Allow-Credentials说顾客端,顾客端实际允诺能能携带用户信息,否则无法携带。
这里对顾客端实际允诺进行了代码块标注,是为了强调该允诺避免和preflight允诺混为一谈。当两个“繁杂”的布吕马允诺发起的时候,首先,应用程序会发送两个preflight允诺,“试探”一下服务端是否允许该布吕马允诺,如果允许,应用程序才允许该“繁杂”允诺(也就是这里所谓的顾客端实际允诺)紧随preflight允诺之后发起,否则就会被应用程序blocked。
那,按照要求实现下preflight允诺吧。
修改basic_greet.cr,增加OPTIONS实现:
options “/greet” do |env| # Allow `POST /greet`… env.response.headers[“Access-Control-Allow-Methods”] = “POST” # …with `Content-type` header in the request… env.response.headers[“Access-Control-Allow-Headers”] = “Content-type” end 复制代码重启USB服务,控制面板再次允诺,如图:
图中通过Status 200 OK可以看出preflight允诺是获得成功的,但是下面控制面板报错:响应头信息中缺少Access-Control-Allow-Origin信息。也就是说,preflight允诺是获得成功了,CORS协议要求必须存在的preflight允诺响应头信息也存在,但是由于Access-Control-Allow-Origin头信息的缺失,应用程序相混思路限制读取允诺响应内容。
修改basic_greet.cr,响应信息头增加
env.response.headers[“
Access-Control-Allow-Origin”] = “http://bbs.tianya.cn” :options “/greet” do |env| # Allow `POST /greet`… env.response.headers[“Access-Control-Allow-Methods”] = “POST” # …with `Content-type` header in the request… env.response.headers[“Access-Control-Allow-Headers”] = “Content-type” # …from https://www.google.com origin. env.response.headers[“Access-Control-Allow-Origin”] = “http://bbs.tianya.cn” end 复制代码再次允诺:
preflight允诺获得成功,如图:
控制面板还是有顾客端实际允诺报错,不过这个错误就很熟悉了:
响应头信息中缺少Access-Control-Allow-Origin信息被应用程序明令禁止读取响应内容。USB中增加响应header信息:
post “/greet” do |env| name = env.params.json[“name”].as(String) env.response.headers[“Access-Control-Allow-Origin”] = “http://bbs.tianya.cn” “Hello, #{name}!” end 复制代码再次允诺:
“繁杂”允诺的preflight和顾客端实际允诺都获得成功了,实现了“繁杂”允诺的布吕马统计数据共享。
“繁杂”允诺整个心灵心路历程,概括如图: