4 CORS¶
Introducción¶
Cuando se crea un servidor HTTP como podemos hacer con un API REST, y realizamos peticiones a dicho servidor desde otros dominios o servidores, suele ocurrir un error de CORS.
Este tipo de error es un error sencillo de manejar pero que mucho desarrolladores obvian e incluso no entienden a la perfección.
Definición¶
El Cross Origin Resource Sharing (CORS), o bien el cruce de recursos de origen compartido es un mecanismo que te permite que un recurso sea restringido en una página web para evitar que un origen o un dominio fuera de otro dominio desde que se sirvió ese recurso se pueda acceder a él.
El navegador recibe la petición del usuario y le pregunta a la API si un dominio que no es el de la propia API puede realizar la petición. Si en la API no se ha configurado ningún tipo de acceso la API le contesta al navegador que no, por lo que le devuelve al usuario un error de CORS.
Dar permisos de Control de Acceso¶
Para solucionar el problema de CORS debemos modificar la cabecera Access-Control-Allow-Origin. El valor de dicha cabecera se indicará el origen que sí puede acceder a nuestro dominio:
Si se desea permitir el acceso desde cualquier dominio se puede indicar con un asterisco (*
):
Hay que tener en cuenta la seguridad a la hora de dar acceso completo a nuestra web, ya que a veces puede ser una operación peligrosa.
Cabecera Origin¶
Cuando realizamos una petición podemos comprobar su origen a través de la cabecera origin. De esta forma podemos filtrar el acceso y comprobar si es un dominio aceptado de una lista de dominios:
const ACCEPTED_ORIGINS = [
'http://localhost:8080',
'http://localhost:1234',
'https://movies.com',
]
app.get('/movies', (req, res) => {
const origin = req.header('origin')
if(ACCEPTED_ORIGINS.includes(origin)){
res.header('Access-Control-Allow-Origin', origin)
}
})
En el ejemplo anterior, tenemos una lista de dominios que si nos interesa que pueda realizar una petición GET, por lo que comprobamos si el origen de la petición coincide con uno de los dominios de la lista. En caso afirmativo, le devolvemos la respuesta con el acceso permitido.
Sin embargo, si realizamos la petición desde el mismo dominio que nuestra API, la cabecera origin no sé envía, por lo que se debe controlar. En ese caso, si la cabecera origin no está definida, podemos entender que estamos hablando de nuestro mismo dominio:
const ACCEPTED_ORIGINS = [
'http://localhost:8080',
'http://localhost:1234',
'https://movies.com',
]
app.get('/movies', (req, res) => {
const origin = req.header('origin')
if(ACCEPTED_ORIGINS.includes(origin) || !origin){
res.header('Access-Control-Allow-Origin', origin)
}
})
CORS como middleware¶
El control del acceso a nuestro servidor desde otros dominios se puede manejar en cualquier petición que se realice. Por ejemplo, podemos restringir el acceso solo a todos los dominios que realicen una petición POST desde cualquier recurso, o desde un recurso específico.
También se puede usar como middleware, de tal forma que controlemos el acceso para cualquier petición en cualquier método:
const ACCEPTED_ORIGINS = [
'http://localhost:8080',
'http://localhost:1234',
'https://movies.com',
'https://midu.dev'
]
app.use((req, res, next) => {
const origin = req.header('origin')
if(ACCEPTED_ORIGINS.includes(origin) || !origin){
res.header('Access-Control-Allow-Origin', origin)
}
next()
})
Peticiones PRE-Flight¶
Cuando el usuario realiza una petición a nuestra API, hay algunos métodos que requieren un método previo y especial, si ese método devuelve un OK se ejecutará el método solicitado. Este proceso se conoce comúnmente como CORS PRE-Flight, y se debe controlar el CORS tanto en la petición compleja como en la especial.
Los métodos normales son: HEAD, GET y POST, y por lo tanto solo se validaría el CORS en dichos métodos. Los métodos complejos son PUT, PATCH y DELETE, mientras que el método especial e intermedio es OPTIONS. por lo que se debería validar el CORS tanto en dichos métodos como en el método OPTIONS.
app.delete('/movies/:id', (req, res) => {
const origin = req.header('origin')
if (ACCEPTED_ORIGINS.includes(origin) || !origin) {
res.header('Access-Control-Allow-Origin', origin)
}
// ...
})
En el ejemplo anterior, comprobamos el origen de la petición, pero en este caso aunque sea un origen aceptado, nos lanzará un error de CORS, ya que antes de la petición DELETE realiza una petición previa OPTIONS, y al no ser controlada la CORS en dicha petición recibiremos igualmente un error de cORS. Para solucionar dicho problema será necesario comprobarlo en dicho método:
app.delete('/movies/:id', (req, res) => {
const origin = req.header('origin')
if (ACCEPTED_ORIGINS.includes(origin) || !origin) {
res.header('Access-Control-Allow-Origin', origin)
}
// ...
})
app.options('/movies/:id', (req, res) => {
const origin = req.header('origin')
if (ACCEPTED_ORIGINS.includes(origin) || !origin) {
res.header('Access-Control-Allow-Origin', origin)
res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
}
res.send(200)
})