CORS: ¿Cuál es la motivación detrás de la introducción de solicitudes de verificación previa?

El intercambio de recursos de origen cruzado es un mecanismo que permite que una página web haga XMLHttpRequests a otro dominio (desde wikipedia ), y es bastante importante (de mí :).

He estado jugando con CORS durante los últimos días y creo que entiendo muy bien cómo funciona todo.

Así que mi pregunta no es sobre cómo funciona CORS / Preflight, se trata de la razón detrás de proponer Preflight como un nuevo tipo de solicitud . No veo ninguna razón por la cual el servidor A necesite enviar una verificación previa (PR) al servidor B solo para saber si la solicitud real (RR) será aceptada o no, sin duda sería posible que B acepte / rechace RR sin cualquier PR anterior.

Después de buscar bastante encontré esta información en www.w3.org (7.1.5):

Para proteger los recursos contra las solicitudes de origen cruzado que no podían originarse de ciertos agentes de usuario antes de que existiera esta especificación, se realiza una solicitud de verificación previa para garantizar que el recurso conoce esta especificación.

Encuentro que esta es la oración más difícil de entender de la historia. Mi interpretación (mejor debería llamarla ‘mejor suposición’) es que se trata de proteger el servidor B contra las solicitudes del servidor C que no conoce la especificación.

¿Puede alguien explicar un escenario / mostrar un problema que PR + RR resuelve mejor que solo RR?

Pasé un tiempo confundiéndome en cuanto al propósito de la solicitud de verificación previa, pero creo que ya lo tengo.

La idea clave es que las solicitudes de verificación previa no son una cuestión de seguridad . Más bien, son una cosa que no cambia las reglas .

Las solicitudes previas al vuelo no tienen nada que ver con la seguridad, y no tienen ninguna relación con las aplicaciones que se están desarrollando ahora, con conocimiento de CORS. Por el contrario, el mecanismo de verificación previa beneficia a los servidores que se desarrollaron sin conocimiento de CORS, y funciona como un control de cordura entre el cliente y el servidor que ambos son conscientes de CORS. Los desarrolladores de CORS sintieron que había suficientes servidores por ahí que confiaban en la suposición de que nunca recibirían, por ejemplo, una solicitud DELETE entre dominios que inventaron el mecanismo de verificación previa para permitir que ambas partes aceptaran. Consideraron que la alternativa, que habría sido simplemente habilitar las llamadas entre dominios, habría roto demasiadas aplicaciones existentes.

Hay tres escenarios aquí:

  1. Servidores antiguos, que ya no están en desarrollo, y desarrollados antes de CORS. Estos servidores pueden hacer suposiciones de que nunca recibirán, por ejemplo, una solicitud DELETE entre dominios. Este escenario es el principal beneficiario del mecanismo de verificación previa. Sí, estos servicios podrían ser abusados ​​por un agente de usuario malicioso o no conforme (y CORS no hace nada para cambiar esto), pero en un mundo con CORS, el mecanismo de verificación previa proporciona un “control de cordura” adicional para que los clientes y servidores no romper porque las reglas subyacentes de la web han cambiado.

  2. 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 anterior para asegurarse de que funciona correctamente en un mundo entre dominios. Este escenario permite a los servidores optar progresivamente por CORS, por ejemplo, al decir “Ahora permitiré este encabezado en particular”, “Ahora permitiré este verbo HTTP en particular”, “Ahora permitiré que la información de cookies / autenticación sea enviado “, etc. Este escenario se beneficia del mecanismo de verificación previa.

  3. Nuevos servidores que están escritos con conocimiento de CORS. De acuerdo con las prácticas de seguridad estándar, el servidor debe proteger sus recursos frente a 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 las solicitudes de verificación previa?

Las solicitudes previas al vuelo se introdujeron para que los navegadores estuvieran seguros de que estaban tratando con un servidor CORS-aware antes de enviar ciertas solicitudes. Esas solicitudes se definieron como aquellas que eran potencialmente peligrosas (cambio de estado) y nuevas (no posibles antes de CORS debido a la misma política de origen ). El uso de las solicitudes de verificación previa significa que los servidores deben optar (respondiendo adecuadamente a la verificación previa) por los tipos de solicitudes potencialmente peligrosos que CORS hace posibles.

Ese es el significado de esta parte de la especificación : “Para proteger recursos contra solicitudes de origen cruzado que no podían originarse de ciertos agentes de usuario antes de que existiera esta especificación, se realiza una solicitud de verificación previa para garantizar que el recurso conoce esta especificación”.

¿Puedes darme un ejemplo?

Imaginemos que un usuario del navegador A.com sesión en su sitio bancario en A.com . Cuando navegan hacia el B.com malicioso, esa página incluye algo de Javascript que intenta enviar una solicitud DELETE a A.com/account . Dado que el usuario está conectado a A.com , esa solicitud, si se envía, incluirá cookies que identifican al usuario.

Antes de CORS, la política de Same Origin del navegador habría bloqueado el envío de 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 DELETE directamente y dejar todo en el servidor. Pero, ¿qué A.com si A.com no tiene conocimiento del protocolo CORS? Podría seguir adelante y ejecutar el DELETE peligroso. Supuso que nunca podría recibir una solicitud de este tipo debido a la Política de origen del navegador, por lo que nunca se diseñó para frustrar dicho ataque.

Para proteger dichos servidores que no son compatibles con CORS, el protocolo requiere que el navegador primero envíe una solicitud de verificación previa . Este nuevo tipo de solicitud es algo que solo los servidores con CORS pueden responder adecuadamente, lo que permite al navegador saber si es o no seguro enviar el DELETE real.

¿Por qué tanto alboroto sobre el navegador, no puede el atacante simplemente enviar una solicitud DELETE desde su propia computadora?

Claro, pero tal solicitud no incluirá las cookies del usuario. El ataque que 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 como Falsificación de solicitud entre sitios , donde un formulario en el sitio B.com puede A.com en A.com con las cookies del usuario y causar daños.

Está bien. Otra forma de express esto es que las solicitudes de verificación previa se crearon para no boost la superficie de ataque CSRF para los servidores no compatibles con CORS.

Pero mirando los requisitos para las solicitudes “simples” que no requieren comprobaciones previas, veo que POST aún está permitido. ¡Eso puede cambiar el estado y eliminar datos como un DELETE !

¡Es verdad! CORS no protege su sitio de los ataques CSRF. Por otra parte, sin CORS tampoco está protegido de los ataques CSRF. El propósito de las solicitudes de verificación previa es solo limitar su exposición CSRF a lo que ya existía en el mundo pre-CORS.

Suspiro. OK, a regañadientes acepto la necesidad de solicitudes de verificación previa. Pero, ¿por qué tenemos que hacerlo por cada recurso (URL) en el servidor? El servidor maneja CORS o no.

¿Está usted seguro de eso? No es raro que varios servidores manejen solicitudes para un solo dominio. Por ejemplo, puede ser que las solicitudes a A.com/url1 sean manejadas por un tipo de servidor y las solicitudes a A.com/url2 sean manejadas por un tipo diferente de servidor. En general, no es el caso que el servidor que maneja un solo recurso puede ofrecer garantías de seguridad sobre todos los recursos en ese dominio.

Multa. Comprometámonos. Creemos un nuevo encabezado CORS que permita al servidor indicar exactamente los recursos por los que puede hablar, de modo que se puedan evitar solicitudes adicionales de verificación previa a esas URL.

¡Buena idea! De hecho, el encabezado Access-Control-Policy-Path se propuso solo para este propósito. Sin embargo, en última instancia, no se incluyó en 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 serían seguras 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 perder ancho de banda y duplicar la latencia solo para acomodar errores en un servidor en particular en un momento determinado?

Las opiniones difieren

Bueno, al menos los navegadores guardarán en caché la verificación previa para una sola URL.

Sí. Aunque probablemente no por mucho tiempo. En los navegadores WebKit, el tiempo máximo de caché de verificación previa es actualmente de 10 minutos .

Suspiro. Bueno, si sé que mis servidores son CORS-aware, y por lo tanto no necesitan la protección que ofrecen las solicitudes de verificación previa, ¿hay alguna forma de evitarlos?

Su única opción real es asegurarse de cumplir con los requisitos para las solicitudes “simples”. Eso podría significar omitir encabezados personalizados que de lo contrario incluirías (como X-Requested-With ), mentir sobre el tipo de Content-Type , o más.

Hagas lo que hagas, debes asegurarte de tener protecciones CSRF correctas, ya que la especificación CORS no aborda el rechazo de solicitudes “simples”, incluido el POST inseguro. Como lo especifica la especificación : “los recursos para los cuales las solicitudes simples tienen un significado distinto a la recuperación deben protegerse contra la falsificación de solicitudes entre sitios”.

Considere el mundo de solicitudes entre dominios antes de CORS. Puede hacer un formulario estándar POST, o usar un script o una etiqueta de image para emitir una solicitud GET. No puede hacer ningún otro tipo de solicitud que no sea GET / POST, y no puede emitir ningún encabezado personalizado en estas solicitudes.

Con el advenimiento 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 dando a los servidores una forma de optar por cualquier tipo de solicitud nueva. Esta opción es la solicitud de verificación previa.

Por lo tanto, las solicitudes GET / POST sin encabezados personalizados no necesitan 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 estos son nuevos en la especificación CORS. Si el servidor no sabe nada acerca de CORS, responderá sin ningún encabezado específico de CORS, y la solicitud real no se realizará.

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 están preparados para este tipo de solicitudes. La verificación previa de CORS permite que las solicitudes entre dominios se introduzcan en la web de manera segura.

CORS le permite especificar más encabezados y tipos de métodos de lo que era posible anteriormente con origen o

cruzada.

Algunos servidores podrían haber sido (pobremente) protegidos con la suposición de que un navegador no puede realizar, por ejemplo, solicitud DELETE origen cruzado o solicitud de origen cruzado X-Requested-With encabezado X-Requested-With , por lo que dichas solicitudes son “de confianza”.

Para asegurarse de que el servidor realmente soporta CORS y no solo responde a solicitudes aleatorias, se ejecuta la comprobación previa.

Aquí hay otra forma de verlo, usando el código:

    

Pre-CORS, el bash de explotación anterior fallaría porque viola la política de origen idéntico. Una API diseñada de esta manera no necesitaba protección XSRF, ya que estaba protegida por el modelo de seguridad nativo del navegador. Era imposible para un navegador anterior a CORS generar un POSTE JSON de origen cruzado.

Ahora CORS entra en escena: si no se requería optar por CORS a través del pre-vuelo, de repente este sitio tendría una gran vulnerabilidad, sin que fuera culpa suya.

Para explicar por qué algunas solicitudes pueden omitir el pre-vuelo, esta es respondida por la especificación:

Una solicitud simple de origen cruzado se ha definido como congruente con aquellas que pueden ser generadas por agentes de usuario implementados actualmente que no se ajustan a esta especificación.

Para desenredar eso, GET no está previamente definido porque es un “método simple” como se define en 7.1.5. (Los encabezados también deben ser “simples” para evitar el pre-vuelo). La justificación para esto es que la solicitud GET de origen cruzado “simple” ya podría ser ejecutada, por ejemplo, (así es como funciona JSONP). Dado que cualquier elemento con un atributo src puede desencadenar un GET de origen cruzado, sin pre-vuelo, no habría ningún beneficio de seguridad al requerir precombate en XHR "simples".

Siento que las otras respuestas no se centran en la razón por la que la lucha previa mejora la seguridad.

Escenarios:

1) Con pre-vuelo . Un atacante falsifica una solicitud del sitio dummy-forums.com mientras el usuario está autenticado en safe-bank.com
Si el servidor no verifica el origen, y de alguna manera tiene un defecto, el navegador emitirá una solicitud de pre-vuelo, método de OPCIÓN. El servidor no conoce ninguno de los CORS que espera el navegador como respuesta, por lo que el navegador no continuará (sin daño alguno)

2) Sin pre-vuelo . Un atacante falsifica la solicitud en el mismo escenario que el anterior, el navegador emitirá la solicitud POST o PUT de inmediato, el servidor la acepta y podría procesarla, esto podría causar algún daño.

Si el atacante envía una solicitud directamente, origen cruzado, de un host aleatorio, lo más probable es que esté pensando en una solicitud sin autenticación. Esa es una solicitud falsificada, pero no una xsrf. para que el servidor haya verificado las credenciales y falle. CORS no intenta evitar que un atacante que tenga credenciales emita solicitudes, aunque una lista blanca podría ayudar a reducir este vector de ataque.

El mecanismo previo al vuelo agrega seguridad y consistencia entre clientes y servidores. No sé si vale la pena el apretón de manos extra para cada solicitud, ya que el almacenamiento en caché es muy útil, pero así es como funciona.

Además, para los métodos de solicitud HTTP que pueden causar efectos secundarios en los datos del usuario (en particular, para los métodos HTTP distintos de GET, o para el uso POST con ciertos tipos MIME), la especificación exige que los navegadores “comprueben previamente” la solicitud

Fuente

¿No son las solicitudes preeliminadas sobre el rendimiento ? Con las solicitudes con verificación previa, un cliente puede saber rápidamente si la operación está permitida antes de enviar una gran cantidad de datos, por ejemplo, en el método JSON con PUT. O antes de viajar datos confidenciales en los encabezados de autenticación a través del cable.

El hecho de PUT, DELETE y otros métodos, además de los encabezados personalizados, no están permitidos por defecto (necesitan permiso explícito con “Access-Control-Request-Methods” y “Access-Control-Request-Headers”), eso suena solo como una doble verificación, porque estas operaciones podrían tener más implicaciones para los datos del usuario, en lugar de GET. Entonces, suena como:

“Vi que permitía solicitudes entre sitios desde http: //foo.example , PERO ¿está SEGURO de que permitirá las solicitudes DELETE? ¿Consideró los impactos que estas solicitudes podrían causar en los datos del usuario?”

No entendí la correlación citada entre las solicitudes con verificación previa y los beneficios de los servidores antiguos. Un servicio web implementado antes de CORS, o sin conocimiento de CORS, nunca recibirá NINGUNA solicitud entre sitios, porque primero su respuesta no tendrá el encabezado “Access-Control-Allow-Origin”.

En un navegador compatible con CORS, las solicitudes de lectura (como GET) ya están protegidas por la misma política de origen: un sitio web malicioso que intente realizar una solicitud autenticada entre dominios (por ejemplo, el sitio web de banca por Internet o la interfaz de configuración del enrutador) no ser capaz de leer los datos devueltos porque el banco o el enrutador no configuran el encabezado Access-Control-Allow-Origin .

Sin embargo, con solicitudes de escritura (como POST) el daño se realiza cuando la solicitud llega al servidor web. * Un servidor web podría verificar el encabezado de Origin para determinar si la solicitud es legítima, pero esta comprobación a menudo no se implementa porque el servidor web no tiene la necesidad de CORS o el servidor web es anterior a CORS y, por lo tanto, supone que los POST de dominios cruzados están completamente prohibidos por la política de origen idéntico.

Es por eso que los servidores web tienen la oportunidad de optar por recibir solicitudes de escritura entre dominios .

* Esencialmente la versión AJAX de CSRF.