¿Cuál es la motivación detrás de la introducción de solicitudes CORS de verificación previa?
El intercambio de recursos entre orígenes es un mecanismo que permite que una página web realice XMLHttpRequests a otro dominio (de Wikipedia ).
He estado jugando con CORS durante los últimos días y creo que entiendo bastante bien cómo funciona todo.
Entonces, mi pregunta no es sobre cómo funciona CORS/preflight, sino sobre la razón detrás de crear comprobaciones previas como un nuevo tipo de solicitud . No veo ninguna razón por la cual el servidor A necesita enviar una verificación previa (PR) al servidor B solo para saber si la solicitud real (RR) será aceptada o no; sin duda, B podría aceptar/rechazar RR sin cualquier PR previo.
Después de buscar bastante encontré esta información en www.w3.org (7.1.5):
Para proteger los recursos contra solicitudes de orígenes cruzados que no podían originarse en ciertos agentes de usuario antes de que existiera esta especificación, se realiza una solicitud de verificación previa para garantizar que el recurso conozca esta especificación.
Creo que esta es la frase más difícil de entender jamás. Mi interpretación (mejor debería llamarla "mejor suposición") es que se trata de proteger el servidor B contra solicitudes del servidor C que no conoce la especificación.
¿Alguien puede explicar un escenario/mostrar un problema que PR + RR resuelve mejor que RR solo?
Pasé algún tiempo confundido sobre el propósito de la solicitud de verificación previa, pero creo que ya lo entendí.
La idea clave es que las solicitudes de verificación previa no son una cuestión de seguridad . Más bien, se trata de no cambiar las reglas .
Las solicitudes de verificación previa no tienen nada que ver con la seguridad y no tienen relación con las aplicaciones que se están desarrollando ahora, con conocimiento de CORS. Más bien, el mecanismo de verificación previa beneficia a los servidores que se desarrollaron sin conocimiento de CORS, y funciona como una verificación de integridad entre el cliente y el servidor para saber que ambos son conscientes de CORS. Los desarrolladores de CORS sintieron que había suficientes servidores que confiaban en el supuesto de que nunca recibirían, por ejemplo, una solicitud DELETE entre dominios, por lo que inventaron el mecanismo de verificación previa para permitir que ambas partes opten por participar. Consideraron que la alternativa, que habría sido simplemente habilitar las llamadas entre dominios, habría interrumpido demasiadas aplicaciones existentes.
Hay tres escenarios aquí:
Servidores antiguos, que ya no están en desarrollo y desarrollados antes de CORS. Estos servidores pueden asumir que nunca recibirán, por ejemplo, una solicitud DELETE entre dominios. Este escenario es el principal beneficiario del mecanismo de verificación previa. Sí, un agente de usuario malicioso o no conforme ya podría abusar de estos servicios (y CORS no hace nada para cambiar esto), pero en un mundo con CORS, el mecanismo de verificación previa proporciona una "verificación de cordura" adicional para que los clientes y servidores no romper porque las reglas subyacentes de la web han cambiado.
Servidores que aún están en desarrollo, pero que contienen una gran cantidad de código antiguo y para los cuales no es factible/deseable auditar todo el código antiguo para asegurarse de que funcione correctamente en un mundo entre dominios. Este escenario permite a los servidores optar progresivamente por CORS, por ejemplo, diciendo "Ahora permitiré este encabezado en particular", "Ahora permitiré este verbo HTTP en particular", "Ahora permitiré que se guarden cookies/información de autenticación". enviado", etc. Este escenario se beneficia del mecanismo de verificación previa.
Nuevos servidores escritos teniendo en cuenta CORS. Según las prácticas de seguridad estándar, el servidor tiene que proteger sus recursos ante cualquier solicitud entrante; los servidores no pueden confiar en que los clientes no hagan cosas maliciosas. Este escenario no se beneficia del mecanismo de verificación previa : el mecanismo de verificación previa no brinda seguridad adicional a un servidor que ha protegido adecuadamente sus recursos.
¿Cuál fue la motivación detrás de la introducción de solicitudes de verificación previa?
Se introdujeron solicitudes de verificación previa para que un navegador pudiera estar seguro de que estaba tratando con un servidor compatible con CORS antes de enviar ciertas solicitudes. Esas solicitudes se definieron como aquellas que eran potencialmente peligrosas (que cambiaban de estado) y nuevas (no posibles antes de CORS debido a la Política del mismo origen ). El uso de solicitudes de verificación previa significa que los servidores deben aceptar (respondiendo adecuadamente a la verificación previa) los tipos de solicitudes nuevos y potencialmente peligrosos que CORS hace posibles.
Ese es el significado de esta parte de la especificación original: "Para proteger los recursos contra solicitudes de origen cruzado que no podían originarse en ciertos agentes de usuario antes de que existiera esta especificación, se realiza una solicitud de verificación previa para garantizar que el recurso conozca esta especificación".
¿Puedes darme un ejemplo?
Imaginemos que un usuario del navegador inicia sesión en su sitio bancario en A.com
. Cuando navegan a la página maliciosa B.com
, esa página incluye algo de Javascript que intenta enviar una DELETE
solicitud A.com/account
. Dado que el usuario ha iniciado sesión A.com
, esa solicitud, si se envía, incluiría cookies que identifican al usuario.
Antes de CORS, la Política del mismo origen del navegador le habría impedido enviar esta solicitud. Pero dado que el propósito de CORS es hacer posible este tipo de comunicación entre orígenes, eso ya no es apropiado.
El navegador podría simplemente enviar el mensaje DELETE
y dejar que el servidor decida cómo manejarlo. Pero ¿qué pasa si A.com
no conoce el protocolo CORS? Podría seguir adelante y ejecutar lo peligroso DELETE
. Podría haber asumido que, debido a la política del mismo origen del navegador, nunca podría recibir tal solicitud y, por lo tanto, es posible que nunca hubiera estado protegido contra tal ataque.
Entonces, para proteger dichos servidores que no son compatibles con CORS, el protocolo requiere que el navegador envíe primero una solicitud de verificación previa . Este nuevo tipo de solicitud es algo a lo que solo los servidores compatibles con CORS pueden responder adecuadamente, lo que permite al navegador saber si es seguro o no enviar el archivo DELETE
.
¿A qué se debe todo este alboroto por el navegador? ¿No puede el atacante simplemente enviar una DELETE
solicitud desde su propia computadora?
Claro, pero dicha solicitud no incluirá las cookies del usuario. El ataque que esto está diseñado para evitar se basa en el hecho de que el navegador enviará cookies (en particular, información de autenticación para el usuario) para el otro dominio junto con la solicitud.
Eso suena a falsificación de solicitudes entre sitiosB.com
, donde se puede enviar un formulario en el sitio A.com
con las cookies del usuario y causar daños.
Así es. Otra forma de decir esto es que las solicitudes de verificación previa se crearon para no aumentar la superficie de ataque CSRF para servidores que no son compatibles con CORS.
Pero POST
está catalogado como un método que no requiere comprobaciones previas. ¡Eso puede cambiar de estado y eliminar datos como un DELETE
!
¡Eso es cierto! CORS no protege su sitio de ataques CSRF. Por otra parte, sin CORS tampoco estás protegido de los ataques CSRF. El propósito de las solicitudes de verificación previa es simplemente limitar su exposición CSRF a lo que ya existía en el mundo anterior a CORS.
Suspiro. Bien, acepto de mala gana la necesidad de realizar solicitudes de verificación previa. Pero, ¿por qué tenemos que hacerlo para cada recurso (URL) del servidor? El servidor maneja CORS o no.
¿Está usted seguro de eso? No es raro que varios servidores manejen solicitudes de un solo dominio. Por ejemplo, puede darse el caso de que las solicitudes sean A.com/url1
manejadas por un tipo de servidor y las solicitudes sean A.com/url2
manejadas por un tipo diferente de servidor. Generalmente no se da el caso de que el servidor que maneja un único recurso pueda ofrecer garantías de seguridad sobre todos los recursos de ese dominio.
Bien. Hagamos concesiones. Creemos un nuevo encabezado CORS que permita al servidor indicar exactamente de qué recursos puede hablar, de modo que se puedan evitar solicitudes de verificación previa adicionales a esas URL.
¡Buena idea! De hecho, el encabezado Access-Control-Policy-Path
se propuso precisamente con este propósito. Sin embargo, al final quedó fuera de la especificación, aparentemente porque algunos servidores implementaron incorrectamente la especificación URI de tal manera que las solicitudes a rutas que parecían seguras para el navegador no lo serían en realidad en los servidores rotos.
¿Fue esta una decisión prudente que priorizó la seguridad sobre el rendimiento, permitiendo a los navegadores implementar inmediatamente la especificación CORS sin poner en riesgo los servidores existentes? ¿O fue miope condenar a Internet a un ancho de banda desperdiciado y una latencia duplicada sólo para acomodar errores en un servidor particular en un momento particular?
Las opiniones difieren.
Bueno, ¿al menos los navegadores almacenarán en caché la verificación previa de una única URL?
Sí. Aunque probablemente no por mucho tiempo. En los navegadores WebKit, el tiempo máximo de verificación previa en caché es actualmente de 10 minutos .
Suspiro. Bueno, si sé que mis servidores son compatibles con CORS y, por lo tanto, no necesitan la protección que ofrecen las solicitudes de verificación previa, ¿hay alguna forma de evitarlas?
Su única opción real es asegurarse de que sus solicitudes utilicen encabezados y métodos seguros para CORS . Eso podría significar omitir encabezados personalizados que de otro modo incluiría (como ), cambiar el o más.X-Requested-With
Content-Type
Hagas lo que hagas, debes asegurarte de contar con las protecciones CSRF adecuadas, ya que CORS no bloqueará todas las solicitudes inseguras. Como dice la especificación original : "los recursos para los cuales las solicitudes simples tienen un significado distinto a la recuperación deben protegerse de la falsificación de solicitudes entre sitios".
Considere el mundo de las solicitudes entre dominios antes de CORS. Puede realizar una POST de formulario estándar o utilizar una etiqueta script
o image
para emitir una solicitud GET. No podía realizar ningún otro tipo de solicitud que no fuera GET/POST y no podía emitir ningún encabezado personalizado en estas solicitudes.
Con la llegada de CORS, los autores de las especificaciones se enfrentaron al desafío de introducir un nuevo mecanismo entre dominios sin romper la semántica existente de la web. Eligieron hacer esto brindando a los servidores una forma de aceptar cualquier tipo de solicitud nueva. Esta aceptación es la solicitud de verificación previa.
Por lo tanto, las solicitudes GET/POST sin encabezados personalizados no necesitan una verificación previa, ya que estas solicitudes ya eran posibles antes de CORS. Pero cualquier solicitud con encabezados personalizados, o solicitudes PUT/DELETE, necesita una verificación previa, ya que son nuevas en la especificación CORS. Si el servidor no sabe nada sobre CORS, responderá sin encabezados específicos de CORS y no se realizará la solicitud real.
Sin la solicitud de verificación previa, los servidores podrían comenzar a ver solicitudes inesperadas de los navegadores. Esto podría generar un problema de seguridad si los servidores no estuvieran preparados para este tipo de solicitudes. La verificación previa de CORS permite que las solicitudes entre dominios se introduzcan en la web de forma segura.