Establecer cookies para solicitudes de origen cruzado

Resuelto Pim van der Heijden asked hace 7 años • 15 respuestas

¿Cómo compartir cookies de origen cruzado? Más específicamente, ¿cómo utilizar el Set-Cookieencabezado en combinación con el encabezado Access-Control-Allow-Origin?

Aquí hay una explicación de mi situación:

Estoy intentando configurar una cookie para una API que se ejecuta localhost:4000en una aplicación web alojada en localhost:3000.

Parece que recibo los encabezados de respuesta correctos en el navegador, pero lamentablemente no tienen ningún efecto. Estos son los encabezados de respuesta:

HTTP/1.1 200 correcto
Control-de-acceso-permitir-origen: http://localhost:3000
Variar: origen, aceptar-codificación
Establecer cookies: token=0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Edad máxima=86400; Dominio=localhost:4000; Ruta=/; Vence = martes 19 de septiembre de 2017 a las 21:11:36 GMT; Sólo Http
Tipo de contenido: aplicación/json; juego de caracteres = utf-8
Longitud del contenido: 180
Etiqueta ET: W/"b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ"
Fecha: lunes 18 de septiembre de 2017 21:11:36 GMT
Conexión: mantener vivo

Además, puedo ver la cookie Response Cookiescuando inspecciono el tráfico usando la pestaña Red de las herramientas para desarrolladores de Chrome. Sin embargo, no puedo ver que se haya configurado una cookie en la pestaña Aplicación en Storage/Cookies. No veo ningún error de CORS, así que supongo que me falta algo más.

¿Alguna sugerencia?

Actualización I:

Estoy usando el módulo de solicitud en una aplicación React-Redux para emitir una solicitud a un /signinpunto final en el servidor. Para el servidor utilizo express.

Servidor expreso:

res.cookie('token', 'xxx-xxx-xxx', { edad máxima: 86400000, httpOnly: verdadero, dominio: 'localhost:3000' })

Solicitar en el navegador:

request.post({ uri: '/signin', json: { nombre de usuario: 'userOne', contraseña: '123456'}}, (err, respuesta, cuerpo) => {
    // haciendo cosas
})

Actualización II:

Ahora estoy configurando encabezados de solicitud y respuesta como loco, asegurándome de que estén presentes tanto en la solicitud como en la respuesta. A continuación se muestra una captura de pantalla. Observe los encabezados Access-Control-Allow-Credentials, Access-Control-Allow-Headersy Access-Control-Allow-Methods. Access-Control-Allow-OriginAl observar el problema que encontré en el github de Axios , tengo la impresión de que todos los encabezados requeridos ahora están configurados. Sin embargo, todavía no hay suerte...

ingrese la descripción de la imagen aquí

Pim van der Heijden avatar Sep 19 '17 04:09 Pim van der Heijden
Aceptado

Enfoque entre sitios

Para permitir recibir y enviar cookies mediante una solicitud CORS con éxito, haga lo siguiente.

Configuración del encabezado HTTP de back-end (servidor):

  • Access-Control-Allow-CredentialsEstablezca el valor del encabezado HTTP en true.

  • Asegúrese de que los encabezados HTTP Access-Control-Allow-Originy Access-Control-Allow-Headersestén configurados. No utilices un comodín *. Cuando configure el origen permitido, asegúrese de utilizar todo el origen, incluido el esquema, es decir, http no es lo mismo que https en CORS.

Para obtener más información sobre cómo configurar CORS en express js, lea los documentos aquí .

Configuración de cookies: Configuración de cookies por actualización de Chrome y Firefox en 2021:

  • SameSite=None
  • Secure

Al hacerlo SameSite=None, la configuración Securees un requisito. Consulte los documentos en SameSite y sobre los requisitos de Secure . También tenga en cuenta que las herramientas de desarrollo de Chrome ahora han mejorado el filtrado y resaltado de problemas con las cookies en la pestaña Red y la pestaña Aplicación.

Front-end (cliente): establezca el XMLHttpRequest.withCredentialsindicador en true; esto se puede lograr de diferentes maneras según la biblioteca de solicitud-respuesta utilizada:

  • ES6 fetch() Este es el método preferido para HTTP. Usar credentials: 'include'.

  • jQuery 1.5.1 Mencionado con fines heredados. Usar xhrFields: { withCredentials: true }.

  • axios Como ejemplo de una biblioteca NPM popular. Usar withCredentials: true.

Enfoque de proxy

Evite por completo tener que hacer cosas entre sitios (CORS). Puedes lograr esto con un proxy. Simplemente envíe todo el tráfico al mismo nombre de dominio de nivel superior y enrútelo usando DNS (subdominio) y/o equilibrio de carga. Con Nginx esto supone un esfuerzo relativamente pequeño.

Este enfoque es una combinación perfecta con JAMStack. JAMStack dicta que el código API y de aplicación web esté completamente desacoplado por diseño. Cada vez más usuarios bloquean las cookies de terceros. Si la API y la aplicación web se pueden servir fácilmente en el mismo host, el problema de terceros (entre sitios/CORS) se disuelve. Lea sobre JAMStack aquí o aquí .

Nota al margen

Resultó que Chrome no configurará la cookie si el dominio contiene un puerto. Configurarlo para localhost(sin puerto) no es un problema. ¡Muchas gracias a Erwin por este consejo!

Pim van der Heijden avatar Sep 25 '2017 19:09 Pim van der Heijden

Nota para el navegador Chrome lanzada en 2020.

Una versión futura de Chrome solo entregará cookies con solicitudes entre sitios si están configuradas con SameSite=Noney Secure.

Entonces, si su servidor backend no establece SameSite=None, Chrome usará SameSite=Lax de forma predeterminada y no usará esta cookie con solicitudes { withCredentials: true }.

Más información https://www.chromium.org/updates/same-site .

Los desarrolladores de Firefox y Edge también quieren lanzar esta función en el futuro.

La especificación se encuentra aquí: https://datatracker.ietf.org/doc/html/draft-west-cookie-incrementalism-01#page-8

LennyLip avatar Jul 04 '2020 08:07 LennyLip

Para que el cliente pueda leer cookies de solicitudes de origen cruzado, debe tener:

  1. Todas las respuestas del servidor deben tener lo siguiente en su encabezado:

    Access-Control-Allow-Credentials: true

  2. El cliente debe enviar todas las solicitudes con withCredentials: truela opción

En mi implementación con Angular 7 y Spring Boot, lo logré con lo siguiente:


Lado del servidor:

@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true")
@Controller
@RequestMapping(path = "/something")
public class SomethingController {
  ...
}

La origins = "http://my-cross-origin-url.com"parte se agregará Access-Control-Allow-Origin: http://my-cross-origin-url.comal encabezado de respuesta de cada servidor.

La allowCredentials = "true"parte se agregará Access-Control-Allow-Credentials: trueal encabezado de respuesta de cada servidor, que es lo que necesitamos para que el cliente lea las cookies.


Lado del cliente:

import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from 'rxjs';

@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

    constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // send request with credential options in order to be able to read cross-origin cookies
        req = req.clone({ withCredentials: true });

        // return XSRF-TOKEN in each request's header (anti-CSRF security)
        const headerName = 'X-XSRF-TOKEN';
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null && !req.headers.has(headerName)) {
            req = req.clone({ headers: req.headers.set(headerName, token) });
        }
        return next.handle(req);
    }
}

Con esta clase, en realidad inyectas cosas adicionales a todas tus solicitudes.

La primera parte req = req.clone({ withCredentials: true });es lo que necesitas para poder enviar cada solicitud con withCredentials: trueopción. Esto prácticamente significa que primero se enviará una solicitud de OPCIÓN, para que obtenga sus cookies y el token de autorización entre ellas, antes de enviar las solicitudes POST/PUT/DELETE reales, que necesitan este token adjunto (en el encabezado), en orden para que el servidor verifique y ejecute la solicitud.

La segunda parte es la que maneja específicamente un token anti-CSRF para todas las solicitudes. Lo lee de la cookie cuando es necesario y lo escribe en el encabezado de cada solicitud.

El resultado deseado es algo como esto:

respuesta pedido

Stefanos Kargas avatar Jun 13 '2020 19:06 Stefanos Kargas