When I try to make a request GET
, POST
, PUT
, PATCH
,DELETE
from my Frontend I receive from the server
Request from another origin blocked: Same origin policy prevents reading remote resource at http://my_server/ (reason: missing CORS 'Access-Control-Allow-Origin' header).
I have also seen it in English something like:
"No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access."
I've researched WHAT CORS is and the W3C comments on this topic , and I understand what Cross-Origin Resource Sharing means , but I don't understand how it works.
How could I solve this in my WEBS applications?
Finally, I have noticed that my front-end applications generally make two requests, one of type OPTIONS
and the common ones [ GET
, POST
, PUT
, PATCH
....] this is because
I do the tests with test-cors.org and it gives me the same error
CORS is an acronym that refers to the security rules that guarantee communication between two points with a different precedence than the domain from which it is called (ie: different domains).
In general, these rules are implemented by the web client and it is this one that is in charge of validating the delivery of the information or rejecting it based on what the server responds.
In the image shown, these rules are activated when trying to deliver/obtain the information from the server corresponding to "another-domain.mx", if an exchange is not made according to those established on the server, the web client will cancel the request even before anything else.
Access-Control-Allow-Origin: is a header that is returned to indicate if the response can be shared with the requesting domain. You can indicate the domains with which you want to share the information (separated by commas) or an asterisk (*) if you do not know the origin of your applicants.
Note: the asterisk is usually useful if you have an exposed (public) API, otherwise it is recommended not to use it.
PHP code:
Configuration in HTACCESS:
According to information from the MDN , these requests are verified and it is the same browser that executes this OPTIONS call to validate that the request that will be followed by it is valid.
The flow is as follows:
Access-Control-Allow-Methods: POST, DELETE
.The MDN tells us the following:
Example
The following code represents an example of a validation executed when the OPTIONS request is received and it responds to the allowed methods so that the browser can proceed.
PHP code:
Note: the images were built with the Draw.io tool .
This mainly occurs when a server not known by your backend tries to access it.
To be able to solve it will depend on the architecture you have. For example, if you have an api-managment, it would be convenient that in your response header you enable the specific server, or that you give full access.
Now if you don't have it, it will depend on the server you have mounted. In the case of Nodejs there is even a library for it: https://www.npmjs.com/package/cors
Node js example with Expressjs :
the first thing you should do is install it on your server node
Once installed you must configure your main file, let's assume it is called app.js
You must add the clients that can have access, suppose you have a frontend application that runs
localhost
on the port3000
Your config.js would look like this:
This answer aims to explain what CORS is in human language, how it works, show that it is not a trivial issue and that its configuration should not be taken lightly. All this according to my understanding.
I apologize in advance if the answer has gotten too long and if it touches on topics that weren't included in the OP's question.
Regarding the specific questions:
If you already know what CORS is, you can read directly How does CORS work? below.
This part is answered in Preflight Orders (before shipment) .
This full answer deals with what I think needs to be taken into account to enable CORS on a server.
Based on the content and type of access you should determine which headers you will have your server return.
My personal opinion is to restrict authorizations as much as possible, explicitly to servers that you control and that need to have access.
I would verify that the way I am using to control access is not among the cases known to present vulnerabilities. (The ones presented here are probably not a complete list, but enough to give an idea of where to start)
And finally, if the configuration does not work, I would check if my implementation matches any of the common configurations that do not work because they are invalid.
CORS (Cross Origin Resource Sharing)
CORS is a standard for cross-domain sharing of resources in a controlled way.
What are domains?
Domain in the context of CORS is a combination of protocol, host, and port that identifies the server from which we will request resources. For example in "
http://www.example.com:8080
" http is the protocol, www.example.com is the host, and 8080 is the port. The host could alternatively be an IP address and in the absence of a port number, it would be the protocol default (for example 80 for http and 443 for https).What are resources?
Resources are the content that the server returns upon request. It can be html content, css, javascript, fonts, images, xml, pdfs, etc.
Who makes the order?
Yes the user. But how? Using a browser for example. The most generic term for a browser is "
User agent
" (User Agent would be the literal translation).There are different types of "
User agent
" (CURL for example is another type of user agent) but the one that we are going to care aboutCORS
is the web browsers (FireFox, Chrome, Edge, Safari, Opera, etc) due to the fact that they allow to executejavascript
and supportXHR
(Extensible Markup Language / Hypertext Transfer Protocol) and newerFetch
.These
Apis
allow codejavascript
to make resource requests to domains as if it were the browser user typing a url into the address bar .The important difference to note is that when a user enters a url in the address bar , they do so consciously and the result is displayed on the screen.
When the request is made from javascript, it is the visited domain that is typing the request, but it executes it as if it were the user, and the result is received inside the javascript in a variable.
In this case, the user probably has no knowledge of what is going on.
Where is the problem in all this?
The problem is in the way a web browser works and the
javascript
one that the user runs without knowing it.Let's look at an example:
The user has an account in the web application "LosHorarioDeLaFamilia" that is in the domain
A
(A
represents protocol, host, and port, the same asB
below), and where all the members of the family record what they do and what time to be able to coordinate each.The application has a username and password, it asks for a pin to log in to a new device, it is over https, it controls csrf, in short, very secure.
The user logs into the application with his username and password, and the application gives him a session that is saved in a cookie in the browser.
With the url
A/verhorarios
, everyone's schedules are set to organize the day, and then another tab is opened in the browser with the link of an offer that came by mail. Thelink could be
B/ofertamaliciosa
B
is the second domain that is different fromA
where the web app is).When you open this link, a screen is downloaded showing the product, but also code
javascript
that you useXHR
to place an order withA/verhorarios
.The order is being made from the user's browser. Since there is one
cookie
for the domainA
(which has the user's session), thecookie
is sent to the domainA
, the server on the domainA
doesn't see anything different between a request made withXHR
and one made from the address bar, so it validates the session and returns the schedules of the family (in html).The response from the domain is saved in the call
A
variableresultado
XHR
and in the next step, the malicious script makes another oneXHR
to a server controlled by the attacker, sending the obtained result and thus managing to see the user's confidential information.And the user neither found out nor had a practical way to avoid it.
In case it is not clear in the example, the problem is caused by the browser by allowing
javascript
a domain to execute requests to another domain on behalf of the user.But if that were the case, would any site be able to obtain information from other sites frequented by the same browser?
It is true. But browsers already implemented the same-origin policy (
Same Origin
) a long time ago, where they basically preventjavascript
cross-domain execution.So, again, what is CORS and what is it for?
The policy of
Same Origin
directly prevents the cross call ofjavascript
, something that many times you do want to do.An example could be to have the web on one port and the api on another port, making calls from the web to the api. In this case the port changes.
Another example may be subdomains that need to share resources from the main domain. Changing the subdomain changes the host and is considered a different domain.
Then the browsers that implement the standard
CORS
allow to relax the policySame Origin
, so that the browser can check with the domain that provides the resource, if it can be requested from another different domain that we will callOrigin
(Origin).In this way the browser (responsible for the problem) can determine whether to allow the call and/or deliver the received content.
How does CORS work?
The way browsers implement
CORS
it is through a series of headers that are exchanged with the server.When the browser detects a cross-origin order, it adds to the rest of the order headers one
Origin
with the domain fromscript
which the order originator was downloaded.On the other hand, the one
script
who is making the request can indicate that it includescredenciales
or not (not sending credentials is the default behavior).In this context they
credenciales
arecookies
,la cabecera Authorization
(which is used in both basic authentication and bearer/token authentication), andCertificados de cliente
also used for authentication.The expected response from the server will be something like: Since this request
involucra
/no involucra
the sending of credentials, and is originated byX
,se puede
/no se puede
carry forward).This response materializes in one of these ways:
a) Request without credentials
If the request is made without
credenciales
the server, you can send your response:Yo. Without adding any header with what the browser interprets as denying access. Note that this may be due to an explicit deny or because the server does not implement CORS and ignores headers.
ii. Adding a header
Access-Control-Allow-Origin:*
, allowing any domain to access the response.iii. Adding a header
Access-Control-Allow-Origin: dominio
, which specifies thedominio
one that can access.b) Request with credentials
If the request includes
credenciales
, to give access to the resource, in addition to the headerAccess-Control-Allow-Origin
, you must add another headerAccess-Control-Allow-Credentials:true
.And in the header
Access-Control-Allow-Origin
you must specify the domain you give access to (you can't use the wildcard*
).The only value it can take
Access-Control-Allow-Credentials
istrue
, the way to negate the header is not to include it in the response.Header Vary:Origin
If there is more than one domain that can be authorized, then the domain returned by the server is dynamically generated and potentially changes from order to order based on the header
Origin
it receives.In this case, the header must be added to the responses from the server
Vary:Origin
to tell the browser not to cache the responses. This header can also prevent a browser "Cache Poisoning" vulnerability.Preflight orders (prior to shipment)
Browsers can (because not all do) send a prior query to the server as to which methods and headers are allowed for a given origin.
To do this, the browser sends a request
OPTIONS
with the headersOrigin
,Access-Control-Request-Method
, and, if applicable, alsoAccess-Control-Request-Headers
.This should occur when one of these conditions occurs:
1) The order method is neither
GET
norHEAD
.2) The order method is
POST
and the headerContent-Type
is other thanapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
.3) Manually add headers other than:
Accept
,Accept-Language
,Content-Language
, orContent-Type
.The server, if it will accept the request for that origin, responds to a preflight with:
Access-Control-Allow-Origin: dominio
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
(The supported methods)Access-Control-Max-Age: <tiempo-en-segundos>
(The time that keeps the response valid)To these headers would be added:
Access-Control-Allow-Headers: <lista de cabeceras>
(enabled headers separated by commas), if the supported headers have been requested.And/or
Access-Control-Allow-Credentials:true
, if the request will be allowed to be made with credentials.To keep in mind when configuring CORS on the server
CORS configurations not working (Configurations refer to what the server returns as a response)
The following CORS configurations appear to be frequently found on the Internet, but they are invalid configurations.
1) Authorize domain lists separated by spaces
=> Accepted values are *, a single domain, or null (note null is a vulnerability, see below)
2) Enable all domains with credentials
=> As this setting clearly allows any site to execute a request with credentials, it is an invalid setting that will be rejected by browsers.
3) Wildcards (
*
) as part of a domain.=> The intent would be to give you access to any subdomain of example.com, but the wildcard cannot be used in this way.
Known security issues in CORS configuration
This list is intended to show vulnerabilities that a CORS misconfiguration on the server can lead to.
This is not intended to be an exhaustive list but rather examples of what can happen if CORS configuration is taken for granted.
1) Skip the control
Access-Control-Allow-Origin:*
withcredenciales
, copying the oneOrigin
from the request to the oneAccess-Control-Allow-Origin
from the response withcredenciales
.It's probably the product of finding a workaround when the alternative of allowing all origins ('
*
') with credentials doesn't work. The problem is that this completely disables theSame Origin
.Related to this case it is worth mentioning the inefficient verification before copying the domain received in the header
Origin
:1.1 Verify that the domain starts with a certain pattern.
=> Check that it starts with: would
https://ejemplo.com
authorize access tohttps://ejemplo.com.otrodominio.com
eg.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.
References:
https://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS
https://portswigger.net/blog/exploiting-cors-misconfigurations-for-bitcoins-and-bounties
As they indicate to you, this is a problem in the configuration of your server where you have your API. This site has the list of solutions to different web servers (Apache, IIS, Nginix, Express.js, etc).
As an example I give you the solution to a server running a node.js application that uses express.
Simply using the following works seamlessly between two servers.
And in the index.js (from the backend), the following:
Regarding the toptions, they are usually "added" when sending requests, such as the headers, which you will need for your CORS.
Are you using any frameworks like Angular or library like React/Vue?
I have an explanatory link about it at: https://code.i-harness.com/es/q/1cda4c5
To fix the problem with
NodeJS
andExpress
I use this code in the app.js configuration file that I defined previously.With a middleware like this: