当我尝试从我的前端发出请求 时,GET
我POST
从PUT
PATCH
DELETE
服务器收到
来自另一个来源的请求被阻止:同源策略阻止读取http://my_server/上的远程资源(原因:缺少 CORS 'Access-Control-Allow-Origin' 标头)。
我也曾在英语中看到过类似的内容:
“请求的资源上不存在‘Access-Control-Allow-Origin’标头。因此,不允许访问原点‘null’。”
我研究了CORS 是什么以及W3C 对此主题的评论,我了解跨域资源共享的含义,但我不明白它是如何工作的。
如何在我的 WEBS 应用程序中解决这个问题?
最后,我注意到我的前端应用程序通常会发出两个请求,一个是 type 请求,一个是OPTIONS
常见请求 [ GET
, POST
, PUT
, PATCH
....] 这是因为
我用test-cors.org进行测试,它给了我同样的错误
CORS是一个首字母缩略词,指的是保证两点之间通信的安全规则,其优先级不同于调用它的域(即:不同的域)。
通常,这些规则是由 Web 客户端实现的,正是这一规则负责根据服务器的响应来验证信息的传递或拒绝它。
如图所示,这些规则在尝试从服务器传递/获取“another-domain.mx”对应的信息时被激活,如果没有按照服务器上建立的规则进行交换,Web客户端将取消甚至在其他任何事情之前提出请求。
Access-Control-Allow-Origin:是一个返回的标头,用于指示响应是否可以与请求域共享。如果您不知道申请人的来源,您可以指明要与之共享信息的域(以逗号分隔)或星号 (*)。
注意:如果您有公开的(公共)API,星号通常很有用,否则建议不要使用它。
PHP代码:
HTACCESS 中的配置:
根据来自MDN的信息,这些请求是经过验证的,并且执行此 OPTIONS 调用以验证其后面的请求是否有效的是同一浏览器。
流程如下:
Access-Control-Allow-Methods: POST, DELETE
.MDN告诉我们以下内容:
例子
以下代码表示接收到 OPTIONS 请求并响应允许的方法以使浏览器可以继续时执行的验证示例。
PHP代码:
注意:图像是使用Draw.io工具构建的。
这主要发生在后端不知道的服务器尝试访问它时。
能否解决它取决于您拥有的架构。例如,如果您有一个 api-management,您可以方便地在响应标头中启用特定服务器,或者提供完全访问权限。
现在,如果您没有它,它将取决于您安装的服务器。对于 Nodejs,甚至还有一个库:https ://www.npmjs.com/package/cors
带有Expressjs的Node js示例:
您应该做的第一件事是将它安装在您的服务器节点上
安装后,您必须配置主文件,假设它名为 app.js
您必须添加可以访问的客户端,假设您有一个
localhost
在端口上运行的前端应用程序3000
你的config.js看起来像这样:
这个答案旨在解释 CORS 在人类语言中是什么,它是如何工作的,表明它不是一个微不足道的问题,它的配置不应该掉以轻心。这一切都根据我的理解。
如果答案太长并且涉及到OP的问题中未包含的主题,我会提前道歉。
关于具体问题:
如果你已经知道 CORS 是什么,可以直接阅读 CORS 是如何工作的?以下。
这部分在预检订单(装运前)中得到答复。
这个完整的答案涉及我认为在服务器上启用 CORS 需要考虑的因素。
根据访问的内容和类型,您应该确定服务器将返回哪些标头。
我个人的意见是尽可能地限制授权,明确地限制在你控制的并且需要访问的服务器上。
我将验证我用来控制访问的方式不在已知的存在漏洞的案例中。(这里提供的可能不是一个完整的列表,但足以让您了解从哪里开始)
最后,如果配置不起作用,我会检查我的实现是否与任何常见的配置匹配不起作用,因为它们无效。
CORS(跨源资源共享)
CORS 是一种以受控方式跨域共享资源的标准。
什么是域?
CORS 上下文中的域是协议、主机和端口的组合,用于标识我们将从其请求资源的服务器。例如“
http://www.example.com:8080
”中http是协议,www.example.com是主机,8080是端口。主机也可以是 IP 地址,在没有端口号的情况下,它将是协议默认值(例如 http 为 80,https 为 443)。什么是资源?
资源是服务器根据请求返回的内容。它可以是 html 内容、css、javascript、字体、图像、xml、pdfs 等。
谁下订单?
是的,用户。但是怎么做?例如使用浏览器。浏览器最通用的术语是“
User agent
”(用户代理是直译)。有不同类型的“
User agent
”(例如 CURL 是另一种类型的用户代理),但我们要关心CORS
的是网络浏览器(FireFox、Chrome、Edge、Safari、Opera 等),因为它们允许执行javascript
和支持XHR
(可扩展标记语言/超文本传输协议)和更新Fetch
的 .这些
Apis
允许代码javascript
向域发出资源请求,就像浏览器用户在地址栏中键入 url 一样。需要注意的重要区别是,当用户在地址栏中输入 url 时,他们是有意识地这样做的,结果会显示在屏幕上。
当从 javascript 发出请求时,输入请求的是访问域,但它会像用户一样执行它,结果在 javascript 中的变量中接收。
在这种情况下,用户可能不知道发生了什么。
这一切的问题在哪里?
问题在于网络浏览器的工作方式以及
javascript
用户在不知情的情况下运行的方式。让我们看一个例子:
A
用户在域(A
代表协议、主机和端口,下同)的Web应用程序“LosHorarioDeLaFamilia”中有一个帐户B
,并且所有家庭成员都记录了他们所做的事情以及何时能够协调每一个。该应用程序有一个用户名和密码,它要求一个pin来登录到一个新设备,它是通过https,它控制csrf,总之,非常安全。
用户使用他的用户名和密码登录到应用程序,应用程序给他一个会话,该会话保存在浏览器的 cookie 中。
使用 url
A/verhorarios
,每个人的日程安排都被设置为组织一天,然后在浏览器中打开另一个选项卡,其中包含通过邮件发送的报价链接。该链接可能是
B/ofertamaliciosa
B
A
是与 Web 应用程序不同的第二个域)。当您打开此链接时,将下载一个显示产品的屏幕,
javascript
以及您用于XHR
下订单的代码A/verhorarios
。订单是从用户的浏览器发出的。由于
cookie
域A
(具有用户会话)有一个,因此cookie
被发送到域A
,域上的服务器在使用地址栏发出的请求和从地址栏发出A
的请求之间看不到任何不同,因此它验证XHR
会话并返回家庭的日程安排(在 html 中)。来自域的响应保存在调用
A
变量中resultado
XHR
下一步,恶意脚本XHR
向攻击者控制的服务器发送另一个脚本,将获取的结果发送出去,从而设法查看用户的机密信息。用户既没有发现也没有实用的方法来避免它。
如果在示例中不清楚,则问题是由浏览器导致的,因为它允许
javascript
域代表用户执行对另一个域的请求。但如果是这样的话,任何站点都能够从同一浏览器经常访问的其他站点获取信息吗?
是真的。
Same Origin
但是浏览器很久以前就已经实现了同源策略( ),它们基本上阻止javascript
了跨域执行。那么,再一次,什么是 CORS,它的用途是什么?
的策略
Same Origin
直接防止了 的交叉调用javascript
,这是您多次想做的事情。一个例子可能是让 web 在一个端口上,而 api 在另一个端口上,从 web 调用 api。在这种情况下,端口会发生变化。
另一个例子可能是需要从主域共享资源的子域。更改子域会更改主机并被视为不同的域。
然后实现该标准的浏览器
CORS
允许放宽策略Same Origin
,以便浏览器可以检查提供资源的域,如果它可以从我们将调用的另一个不同的域Origin
(Origin)请求。以这种方式,浏览器(对问题负责)可以确定是否允许呼叫和/或传递接收到的内容。
CORS 是如何工作的?
浏览器实现
CORS
它的方式是通过一系列与服务器交换的标头。当浏览器检测到跨域订单时,它会在其余的订单标题中添加一个带有下载订单发起者
Origin
的域的标题。 另一方面,发出请求的人可以表明它是否包含(不发送凭据是默认行为)。 在这种情况下,它们是,(用于基本身份验证和承载/令牌身份验证),也用于身份验证。script
script
credenciales
credenciales
cookies
la cabecera Authorization
Certificados de cliente
来自服务器的预期响应将类似于:由于此请求
involucra
/no involucra
发送凭据,并且由X
,se puede
/no se puede
继承)。此响应以下列方式之一实现:
a) 没有凭据
的请求 如果请求是在没有
credenciales
服务器的情况下发出的,您可以发送响应:哟。不添加任何带有浏览器解释为拒绝访问的标题。请注意,这可能是由于显式拒绝或因为服务器未实现 CORS 并忽略标头。
ii. 添加 header
Access-Control-Allow-Origin:*
,允许任何域访问响应。iii. 添加一个 header
Access-Control-Allow-Origin: dominio
,它指定dominio
可以访问的那个。b) 带有凭据
的请求 如果请求包括
credenciales
, 要授予对资源的访问权限,除了 header 之外Access-Control-Allow-Origin
,您还必须添加另一个 headerAccess-Control-Allow-Credentials:true
。在标头中,
Access-Control-Allow-Origin
您必须指定您授予访问权限的域(您不能使用通配符*
)。它唯一可以取的值
Access-Control-Allow-Credentials
是true
,否定标头的方法是不将其包含在响应中。标题变化:起源
如果可以授权的域不止一个,则服务器返回的域是动态生成的,并且可能会根据
Origin
其接收到的标头从一个顺序更改为另一个顺序。在这种情况下,必须将标头添加到来自服务器的响应中,
Vary:Origin
以告诉浏览器不要缓存响应。此标头还可以防止浏览器“缓存中毒”漏洞。预检订单(装运前)
浏览器可以(因为并非所有浏览器都这样做)向服务器发送事先查询,以了解给定来源允许哪些方法和标头。
为此,浏览器会发送一个
OPTIONS
带有标头的请求Origin
,Access-Control-Request-Method
和,如果适用的话,还有Access-Control-Request-Headers
。当发生以下情况之一时,应该会发生这种情况:
1) 排序方法既不是
GET
也不是HEAD
。2) order 方法是
POST
,headerContent-Type
不是application/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
。3) 手动添加以下标题:
Accept
、Accept-Language
、Content-Language
或Content-Type
.如果服务器接受该来源的请求,则响应预检:
Access-Control-Allow-Origin: dominio
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
(支持的方法)Access-Control-Max-Age: <tiempo-en-segundos>
(保持响应有效的时间)将向这些标题添加:
Access-Control-Allow-Headers: <lista de cabeceras>
(启用的标头以逗号分隔),如果已请求支持的标头。和/或
Access-Control-Allow-Credentials:true
,如果允许使用凭据发出请求。在服务器上配置 CORS 时要牢记
CORS 配置不起作用(配置是指服务器作为响应返回的内容)
以下 CORS 配置在 Internet 上似乎很常见,但它们是无效配置。
1)授权域列表以空格分隔
=> 接受的值为*、单个域、或null(注意null是漏洞,见下文)
2) 使用凭据启用所有域
=> Como esta configuración claramente permite a cualquier sitio ejecutar un pedido con credenciales, es una configuración inválida que será rechazada por los navegadores.
3) Comodines (
*
) como parte de un dominio.=> La intención sería darle acceso a cualquier subdominio de example.com, pero el comodín no puede usarse de esta forma.
Problemas conocidos de seguridad en configuración de CORS
Esta lista tiene el propósito de mostrar vulnerabilidades que puede generar una mala configuración de CORS en el servidor.
No pretende ser una lista exhaustiva sino ejemplos de lo que puede pasar si la configuración de CORS se toma como algo trivial.
1) Saltar el control
Access-Control-Allow-Origin:*
concredenciales
, copiando elOrigin
del pedido alAccess-Control-Allow-Origin
de la respuesta concredenciales
.Probablemente sea el producto de encontrar una solución cuando la alternativa de permitir todos los orígenes ('
*
') con credenciales no funciona. El problema es que de esta forma se desactiva completamente la política deSame Origin
.Relacionado a este caso vale la pena mencionar el hacer una verificación ineficiente antes de copiar el domino recibido en la cabecera
Origin
:1.1 Verificar que el dominio comience on un patrón determinado.
=> Verificar que comience con:
https://ejemplo.com
autorizaría acceso ahttps://ejemplo.com.otrodominio.com
por ejemplo.1.2 Verficiar que el dominio termine con un patrón determinado.
=> Verificar que termine en
ejemplo.com
autorizaría acceso ahttps://otroejemplo.com
1.3 Expresiones regulares.
=> El problema con la expresión regular es que si aparte de reconocer el patrón del dominio o subdominio, da lugar a reconocer algo más, ese algo más podría usarse para engañar al servidor.
1.4 Ya se mencionó antes: No incluir la cabecera
Vary:Origin
=> Si el dominio indicado con
Access-Control-Allow-Origin
se genera en forma dinámica dependiendo de la cabeceraOrigin
, siempre debe devolvese la cabeceraVary:Origin
para evitar que se cache el resultado.2) Devolver la cabecera
Access-Control-Allow-Origin: null
En un pedido de origen cruzado, el navegador colocará como
Origin
, el dominio deljavascript
que está ejecutando el pedido, pero este, haciendo un manejo conveniente de redirecciones, puede provocar que al servidor le llegue elOrigin:null
.Si el servidor responde (equivocadamente)
Access-Control-Allow-Origin: null
para indicar que no da acceso, el navegador interpretará que SÍ está dando acceso al pedido.3) Anular la conexión segura sobre
https
Si los pedidos a un dominio de confianza son sobre
https
entonces debe validarse que el protocolo autorizado también sea https (no http).4) Utilizar
Access-Control-Allow-Origin:*
(sincredenciales
)Devolviendo esta cabecera, el código javascript no podrá hacer pedidos que requieran credenciales, pero todavía quedan pueden darse los siguientes escenarios:
4.1 Utilizar el navegador como proxy para hacer pedidos a la intranet del usuario.
=> Si el usuario estuviera corriendo un servidor en el puerto 8080 localmente y devolviendo esta cabecera, el sitio remoto podría ejecutar pedidos como http://127.0.0.1:8080, algo que en principio parece imposible.
Lo mismo si el usuario tuviera acceso a una intranet (sin
credenciales
).=> También permite aprovechar potenciales amenazas producto de otras fallas de configuración.
参考:
https ://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS
https://portswigger.net/blog/exploiting-cors-misconfigurations-for-bitcoins-and-bounties
正如他们向您指出的那样,这是您拥有 API 的服务器配置中的一个问题。该站点有针对不同 Web 服务器(Apache、IIS、Nginix、Express.js 等)的解决方案列表。
作为示例,我为您提供了运行使用 express 的 node.js 应用程序的服务器的解决方案。
只需使用以下内容即可在两台服务器之间无缝工作。
在 index.js(来自后端)中,以下内容:
关于 toptions,它们通常在发送请求时“添加”,例如 CORS 需要的标头。
你是否在使用像 Angular 这样的框架或像 React/Vue 这样的库?
我有一个关于它的解释性链接:https ://code.i-harness.com/es/q/1cda4c5
为了解决这个问题,
NodeJS
我Express
在之前定义的 app.js 配置文件中使用了这段代码。使用这样的中间件: