为什么浏览器要限制跨域访问

励志名言 专家建议 2023-08-07 00:48:38 -
谷歌浏览器如何实现Ajax跨域访问

本篇文章给大家谈谈为什么浏览器要限制跨域访问,以及http访问跨域解决办法对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。

本文目录

  1. 为什么浏览器要限制跨域访问
  2. 如何利用JS使用链接跨域下载图片
  3. option和http跨域哪个好
  4. 如何实现跨域及其原理

为什么浏览器要限制跨域访问

背景

TedNelson曾经谈过文学二神——读者和作家,它们都是无敌的:作家可以想写什么就些什么,而读者可以选择什么也不读。不过现在我们有三个神,读者、作家还有中间人,它们任一方都不是无所不能的。

浏览器一直在做的假设是,任何时候只要用户开始使用互联网应用,这个应用就开始尝试攻击这个用户,也就是说互联网应用是默认不可信的,浏览器在它们可能做的任何事情上都加了限制,而且也对互联网应用开发者提供有限的关于这些限制的报错信息。因此这是一个对程序非常不友善的环境,除非这个程序是一个很简单的不访问任何互联网数据的程序。

跨站脚本攻击(XSS)

最早迫使浏览器采用不信任互联网应用这个设计思想的攻击方式,就是跨站点脚本攻击。在这种攻击中,一个恶意的人员刀疤哥会向普通用户爱丽丝发送一封电子邮件,邮件里有一个指向刀疤哥的网站的链接。爱丽丝毫无防备心地点击链接,让浏览器加载网页刀疤网,浏览器允许JavaScript程序默认在任何网页上运行,因此刀疤网上会有一个JS程序在爱丽丝的设备上运行,访问爱丽丝设备上她能访问的一些数据,然后秘密发送数据到刀疤哥的服务器上。

程序可以通过各种方式访问爱丽丝的私有数据。一种方式是爱丽丝正在使用的计算机可能位于防火墙内,该防火墙允许她进行隐式访问她家中的网络摄像头或她大学的期刊库等等资源。之所以说隐式访问,是因为这些访问可以在没有与爱丽丝进行任何交互的情况下完成,或者也可以通过要求她登录系统以让程序获得一些凭据(例如Cookie)来完成。网站刀疤网也可能是她使用的某个银行的官网的虚假版本,一个钓鱼网站,它要求她以某种借口向银行或社交网络进行身份验证。这个钓鱼网站将数据返回给刀疤哥的方法有很多,例如将窃取到的数据放到刀疤网的URL后面,发送GET请求,这样刀疤网的服务器就能拿到数据。跨站脚本攻击可能有很多变种,这里说的是最一般的思路。

跨域资源共享(CORS)

浏览器开发者的第一个冲动可能就是完全阻止JavaScript程序进行任何互联网访问,这样它们就没法偷偷上传用户数据了。但显然互联网应用的开发者需要他们的JS程序能够进行网络访问。例如,银行肯定需要加载一个程序,通过上传用户输入的信息来向银行服务器请求关于这个用户的不同日期、不同账户的更多数据。显然必须允许上传数据才能实现这些交互。

但我们不会允许这些数据被上传到刀疤哥的服务器上,这就是同源策略,只要程序在同一个互联网域名(如http://www.icbc.com.cn)的网页中运行,那么程序可以与这个域名下的任何服务器地址进行交互。URI中的协议和域名就组成了我们所说的「源」。因此同源政策(SameOriginPolicy,SOP)说的就是,来自某一个服务器「源」的数据,以及来自这个服务器的程序的数据,都与来自任何其他源的任何数据分开。这使银行的程序能够很好地运作,又不会暴露隐私。

有什么场景之中同源策略是不可用吗?这还是有的,任何需要程序去访问其他域名下的数据的场景都会被同源策略阻碍到。例如如果有一个网站想提供一个JavaScript程序来检验、测试或者仅仅访问另一个网页,那么它没法访问那个网站上的数据,也就没法达到它的设计目的。

另一个例子是数据融合。当政府开始公开大量的开放公共数据时,有一些网站会开始涌现,这些网站从许多不同的开放数据站点加载数据并提供数据的「融合」——组合许多不同来源的数据,并提供可视化,从而让用户享受到单一数据源所不能提供的洞察力。但实际上,典型的纯前端的数据融合网站现在已经无法工作了。

那么有什么替代方案?浏览器制造商实现了一些钩子以允许数据在不同的源上共享,并称之为跨域资源共享(CORS)。核心问题是——如何在浏览器中区分数据,区分私人的网络摄像头数据和公开的政府开放数据?我们无法改变网络摄像头,但我们可以改变开放数据发布者。浏览器制造商现在要求开放数据发布者在HTTP响应中为任何完全开放的数据添加特殊的CORS头:

Access-control-allow-Origin:*

同时他们添加了一项功能,允许数据发布者指定有限的其他可信来源,这些来源将被允许访问数据发布者产生的数据。

例如银行可以允许可信的信用卡公司的程序访问银行的源下的用户数据,这可以使得运营银行变得更容易:

Access-control-allow-Origin:creditcardcompany.example.com

这意味着发布公开数据的人需要在他们的任何HTTP响应里加上

Access-control-allow-Origin:*

这意味着给互联网上随机的开放数据者带来大量的工作量,可能这些开放数据提供方会因为各种原因没法给所有响应都加上这一条响应头。这使得他们的数据只能被浏览器直接访问,而没法被互联网应用利用。

浏览器事实上不直接查看这些响应头,而是在一个前驱(Pre-flight)的OPTIONS请求里查看,这个请求会自动插入到其他主要的请求之前。所以当开发者在开发者工具里看到主要的请求时,其实已经有几轮请求发生了。

响应头拦截

除了阻止访问数据以外,CORS系统还会阻止不同源的服务器的响应头发送给互联网应用。如果不想被阻止,服务器必须加上另一个响应头:

Access-Control-Allow-Headers:Authorization,User,Location,Link,Vary,Last-Modified,ETag,Accept-Patch,Accept-Post,Updates-Via,Allow,WAC-Allow,Content-Length,WWW-Authenticate上述的响应头里必须包含一些东西,比如「Link」。这些是一般会被浏览器阻止的响应头,你也可以把任何其他的应用和服务器需要因其他目的而是用的响应头加进去。

HTTP方法拦截

作为习题留给读者思考。

例子

官方的示例SoLiD服务器通过这种方式来允许跨域资源访问。

对CORS的调整

这里我们要说的调整是:CORS的设计者事实上故意使其变得更加难使用。

有人会一种感觉,我明白,就是如果允许数据发布者简单地把ACAO:*加在在他们发布的内容上,这会是一个,让用户很容易自废武功的设计。

这里的「用户」当然不是普通用户,而是指配置网络服务器的系统管理员。我们担心的是系统管理员会发现浏览器封锁了对其数据的访问,为了解决这个问题,他们只会在任何地方都加上这个响应头,即使某些数据实际上并不应该被公开。例如,他们提供了不同版本的页面给不同的用户,此时保持用户之间的数据隔离是很重要的,但他们会受到诱惑用ACAO:*来把所有数据都标识成可以访问的。

因此,只要传入的请求中带有用户凭据信息,浏览器就会阻止服务器使用ACAO:*。每当用户「登录」时,如果你愿意,明确地使用他们登录时上传的身份信息。

但是,有时系统需要访问来自另一个源的用户私有的信息,例如前述的银行的例子。对于这种使用凭据的情况,只允许访问Access-Control-Allow-Origin响应头中明确指定了的源。

麻烦的是HTTPS:互联网现在分为两个网络,一个是我们用来登录和传递凭证的网络,而另一个是低安全性的网络。问题在于,如果你开发的不是最终用户顶级应用程序,而是一个中间件,一个代码库,你只能调用浏览器来做网络操作,以及用于处理密码或TLS的浏览器API,必要时得进行登录。中间件的代码没法知道整个过程。

这意味着,如果你的服务器发布的是完全公开的数据,例如政府开放数据,你希望任何代码都能够访问你的数据,系统管理员之间的经验法则是你应该总是回应任何请求头相同的源。相比于用这个:

Access-control-allow-origin:*

所有的开放数据服务器应该发送这个:

Access-control-allow-origin:$(RECEIVED_ORIGIN)

此处的$(RECEIVED_ORIGIN)就用请求头中的源来替换。

这可能会比向所有公共数据服务器添加固定字段更复杂。不过这是一个进步,可以让代码来干活,而不是简单地让人去改改配置——这需要让每一个开放数据发布者都配合。

要向数据发布者解释清楚这些东西简直像打一场战役,战役的结果是到处都可以搜到这些代码片段。事实上Apache都把这个搞成了一个内部的环境变量[@@ref]

难道没有更好的设计来设置静态标头吗,例如

Access-control-allow-origin:PUBLIC_AND_UNCUSTOMIZED

这样系统管理员就不会把一些私密的东西随便暴露出去了,或者也可以用用户的身份来定制化?可能有的人会这么想。不过这就是现在CORS实现的方式。

所以,世界上的数据发布者都开始把CORS源配置通过反射添加到响应头里了。

但一旦你要使用反射式加源的响应头,很关键的一点事允许把Origin加到Vary:响应头里,如果你有Vary:的话。如果没有,就加上一个:

Vary:Origin到每一个通过反射来配置ACAO的响应头里。

不然的话,这里有一个失败的例子:

站点A上的程序向服务器请求公共开放数据

服务器使用ACAO响应头头响应数据

浏览器缓存该响应

用户使用站点B上的其他程序查看相同的数据

浏览器使用缓存副本,但其上的源A与请求站点B不匹配。

浏览器以静默方式阻止了请求,用户和开发者感到十分费解

因此,在正常运行的基于CORS的系统中,服务器发送Vary:Origin响应头,并强制浏览器为每个请求它的Web应用程序保留不同的数据副本,这非常具有讽刺意味,因为这些数据副本可能是完全公开的数据,不需要做任何隐私考量。

CORS设计在历史上大多数时候都是整个网络中最糟糕的设计。但现在,那些设计像Solid这样的系统的人必须创建一个不受XSS攻击影响的系统,数据将是在用户的控制下完全对外公开或者完全对外不可见,并且Web应用程序将被各种不同的社交流程视为可信赖的。

后记:对CORS的第二次调整

对CORS的第二次调整是在浏览器还没完全实现完第一次调整的时候发生的。

NotwithstandingissueswiththedesignofCORS,Chromedoesn’tinfactdoitproperly.

Ifyourequestthesameresourcefirstfromoneoriginandthenfromanother,itservesthecachedversion,whichthenfailscordbecausetheOriginandaccess-control-allow-originheadersdon’tmatch.Thisevenwhenthereturnedheadershave“Vary:Origin”,whichshouldpreventthatsamecachedversionbeingreusedforadifferentorigin.

尽管CORS的设计存在问题,但Chrome实际上也并没有正确地实现它。如果你首先从一个源请求相同的资源,然后从另一个源请求相同的资源,浏览器将尝试提供缓存版本,然后由于Origin和access-control-allow-origin响应头不匹配而失败。即使返回的响应头具有Vary:Origin,这也应该防止相同的缓存版本被重用于不同的源。

问题出现于ChromeVersion59.0.3071.115(OfficialBuild)(64-bit)

火狐在2018-07也出了同样的问题。

如何利用JS使用链接跨域下载图片

其实这个问题归根到底就是跨域问题,那么解决之前我们先明确一下什么叫跨域。

跨域就是从一个域名内的网页去请求另一个域名内的资源,比如你在百度去请求谷歌,这肯定是不可以的,这是js的一种安全策略。

解决方法:

1.被请求方服务器添加跨域资源共享允许头,既在响应头添加Access-Control-Allow-Origin。

2.使用jsonp跨域请求。

3.可以使用vue.js,angular.js这类的去请求,自带路由可以避免跨域。

4.最简单的,在java建一个中间件,使用http去请求跨域的图片资源,然后js去请求java。

option和http跨域哪个好

跨域请求必须用的,而且传输的内容很小,对性能影响不大。

如何实现跨域及其原理

一、什么是跨域?1.什么是同源策略及其限制内容?

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。同源策略限制内容有:

Cookie、LocalStorage、IndexedDB等存储性内容DOM节点AJAX请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

<imgsrc=XXX><linkhref=XXX><scriptsrc=XXX>2.常见跨域场景

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议,域名和端口必须匹配”。

这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么Ajax就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止CSRF,因为请求毕竟是发出去了。

二、跨域解决方案1.jsonp1)JSONP原理

利用<script>标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以。

2)JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)

3)JSONP优缺点

JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

4)JSONP的实现流程声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')。最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。

在开发中可能会遇到多个JSONP请求的回调函数名是相同的,这时候就需要自己封装一个JSONP函数。

上面这段代码相当于向http://localhost:3000/say?wd=Iloveyou&callback=show这个地址请求数据,然后后台返回show('我不爱你'),最后会运行show()这个函数,打印出'我不爱你'

5)jQuery的jsonp形式

JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。

2.cors

CORS需要浏览器和后端同时支持。IE8和9需要通过XDomainRequest来实现。

浏览器会自动进行CORS通信,实现CORS通信的关键是后端。只要后端实现了CORS,就实现了跨域。

服务端设置Access-Control-Allow-Origin就可以开启CORS。该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置CORS和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。

1)简单请求

只要同时满足以下两大条件,就属于简单请求

条件1:使用下列方法之一:

GETHEADPOST

条件2:Content-Type的值仅限于下列三者之一:

text/plainmultipart/form-dataapplication/x-www-form-urlencoded

请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器;XMLHttpRequestUpload对象可以使用XMLHttpRequest.upload属性访问。

2)复杂请求

不符合以上条件的请求就肯定是复杂请求了。复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是option方法的,通过该请求来知道服务端是否允许跨域请求。

我们用PUT向后台请求时,属于复杂请求,后台需做如下配置:

接下来我们看下一个完整复杂请求的例子,并且介绍下CORS请求相关的字段

上述代码由http://localhost:3000/index.html向http://localhost:4000/跨域请求,正如我们上面所说的,后端是实现CORS通信的关键。

3.postMessage

postMessage是HTML5XMLHttpRequestLevel2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

页面和其打开的新窗口的数据传递多窗口之间消息传递页面与嵌套的iframe消息传递上面三个场景的跨域数据传递

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

otherWindow.postMessage(message,targetOrigin,[transfer]);

message:将要发送到其他window的数据。targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。transfer(可选):是一串和message同时传递的Transferable对象.这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接下来我们看个例子:http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回"我不爱你"。

4.websocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于TCP协议。但是WebSocket是一种双向通信协议,在建立连接之后,WebSocket的server与client都能主动向对方发送或接收数据。同时,WebSocket在建立连接时需要借助HTTP协议,连接建立好了之后client与server之间的双向通信就与HTTP无关了。

原生WebSocketAPI使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

我们先来看个例子:本地文件socket.html向localhost:3000发生数据和接受数据

5.Node中间件代理(两次跨域)

实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。代理服务器,需要做以下几个步骤:

接受客户端请求。将请求转发给服务器。拿到服务器响应数据。将响应转发给客户端。

我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。

上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{"title":"fontend","password":"123456"}

6.nginx反向代理

实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。

使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

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

先下载nginx,然后将nginx目录下的nginx.conf修改如下:

最后通过命令行nginx-sreload启动nginx

7.window.name+iframe

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的name值(2MB)。

其中a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

b.html为中间代理页,与a.html同域,内容为空。

总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

8.location.hash+iframe

实现原理:a.html欲与c.html跨域相互通信,通过中间页b.html来实现。三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现步骤:一开始a.html给c.html传一个hash值,然后c.html收到hash值后,再把hash值传递给b.html,最后b.html将结果放到a.html的hash值中。同样的,a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

9.document.domain+iframe

该方式只能用于二级域名相同的情况下,比如a.test.com和b.test.com适用于该方式。只需要给页面添加document.domain='test.com'表示二级域名都相同就可以实现跨域。

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

我们看个例子:页面a.zf1.cn:3000/a.html获取页面b.zf1.cn:3000/b.html中a的值

三、总结CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。日常工作中,用得比较多的跨域方案是cors和nginx反向代理

为什么浏览器要限制跨域访问的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于http访问跨域解决办法、为什么浏览器要限制跨域访问的信息别忘了在本站进行查找哦。

谷歌浏览器如何实现Ajax跨域访问