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

投稿 不建议 2023-08-21 16:38:43 -
谷歌浏览器如何实现Ajax跨域访问

今天给各位分享为什么浏览器要限制跨域访问的知识,其中也会对为啥不建议开启跨域进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录

  1. 为什么浏览器要限制跨域访问
  2. 如何降低浏览器安全级别,允许跨域访问
  3. 电脑如何开启允许跨域访问
  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也出了同样的问题。

如何降低浏览器安全级别,允许跨域访问

工具:IE浏览器方法如下:

1、打开IE浏览器,在工具菜单下选择Internet选项,打开Internet选项卡

2、切换到安全选项卡下,点击可信站点,然后单击站点按钮

3、可信站点窗口输入网址到可信站点的区域,点击添加按钮,网址则会添加到网站列表下,最后关闭可信站点窗口

4、还是在安全选项卡下的可信站点,点击自定义级别

5、打开站点区域窗口,找到跨域浏览窗口和框架选择启用

6、在当前窗口中继续往下翻,通过域访问数据源也选择启用,点击确定关闭受信任的站点区域窗口

7、在Internet窗口也点击确定按钮,同时关闭浏览器

8、在请求的js脚本中添加是否允许跨域访问的权限,jQuery.support.cors默认值为true,则代表允许;反之,不允许。设置完成,重新打开浏览器验证即可。

电脑如何开启允许跨域访问

根据问题下面我们来看看详细的步骤,希望能帮助到你。

1.首先我们打开控制面板。

2.然后点击打开【Internet选项】的指令。

3.接着点击选项卡中的【安全】选项卡。

4.选择【本地Internet】,然后点击【自定义级别】。

5.最后点击【启用跨域浏览窗口和框架】,接着点击【确定】就可以了。

前后端分离项目,如何解决跨域问题

前后端分离项目跨域问题是不可避免的。通常情况下前端由React、Vue等框架编写,通过ajax请求服务端API,传输数据用json格式。

那么为什么有跨域的问题呢?解决跨域问题有哪些方式?搞清楚这两个问题我们需要了解一下什么是同源策略。

浏览器的同源策略

同源策略(Sameoriginpolicy)是一种安全约定,是所有主流浏览器最核心也是最基本的安全功能之一。同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能请求对方的资源。同源指的是:域名、协议、端口均相同。

比如我们访问一个网站

http://www.test.com/index.html,

那么这个页面请求如下地址得情况是这样的:

另外,同源策略又分如下两种情况:

DOM同源策略:禁止对不同源的页面DOM进行操作,主要防止iframe的情况。比如iframe标签里放一个支付宝付款的页面,如果没有同源策略,那么钓鱼网站除了域名不同,其他的则可以和支付宝的网站一模一样。

XMLHttpRequest同源策略:禁止使用XHR对象向不同源的服务器发起http请求。比如网站记录了银行的cookie,这个时候你访问了恶意网站,黑客拿到你的cookie,再通过ajax请求之前的银行网站,便可以轻易的拿到你的银行信息。

所以,正是因为有了同源策略,大家的网络环境才相对的安全一些。

跨域问题的解决办法

了解了同源策略,就知道为什么会有跨域问题的产生了,都是为了安全。但是实际研发中,大家还是需要跨域去访问资源。典型的应用场景就是前后端分离的项目了。那么我们如何去解决跨域问题呢?

CORS-跨域资源共享

CORS是一种W3C标准,定义了当产生跨域问题的时候,客户端与服务端如何通信解决跨域问题。实际上就是前后端约定好定义一些自定义的http请求头,让客户端发起请求的时候能够让服务端识别出来该请求是过还是不过。

浏览器将CORS请求分为简单请求和非简单请求:

简单请求

简单请求必须满足以下两个条件:

请求方式必须是HEAD、GET、POST三种方法之一。

Http请求头必须只能是:Accept、Accept-Lanuage、Content-Lanuage、Last-Event-ID、Content-Type,其中Content-Type只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain。

非简单请求

不满足简单请求条件的就是非简单请求。针对非简单请求,浏览器会发起预检请求。预检请求的意思是当浏览器检查到你的页面含有跨域请求的时候,会发送一个OPTIONS请求给对应的服务器,以检测服务器是否允许当前域名的跨域请求。如果服务端允许该域名请求,则返回204或200状态码,浏览器接收到允许请求时候再继续发送对应的GET/POST/PUT/DELETE请求。同时服务器端也会告知浏览器预检请求的缓存时长是多少,在这个时间范围内,浏览器不会再次发起预检请求。

原理基本上就是上面说的这些,实际业务中我们如何通过配置来解决跨域问题呢?基本上常见的就是三种方式:

nginx配置

通常我们在nginx增加如下配置即可解决跨域问题:

用nginx这种方式是最舒服的,不需要客户端和服务端多做其他工作,对代码无入侵。

jsonp

因为script标签是不受浏览器同源策略的影响,允许跨域请求资源(我们的每一个页面都引用了大量第三方js文件)。所以可以利用动态创建script标签,通过src属性发起跨域请求,这就是jsonp的原理。但是jsonp只支持GET请求,所以并不是一种好的方式。

服务端代码控制

可以在服务端增加对跨域请求的支持:

这种方式相当于全局过滤器,对所有请求都过滤一遍。

以上三种方式都可以一定程度上解决跨域问题,但是nginx配置和服务端控制不能同时存在,否则会报“Access-Control-Allow-OriginNotAllowMultiplevalue”的错误。个人比较推荐nginx配置的方式,一劳永逸,不需要每个web项目都去编写跨域的代码。

大家在工作中有没有遇到过跨域问题呢?都是怎么解决的?欢迎评论区交流讨论,共同学习~

关于为什么浏览器要限制跨域访问的内容到此结束,希望对大家有所帮助。

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