¿Qué significa y qué implica CORS?
Antes de definir qué significa CORS y sus siglas, es necesario entender el concepto de un agente de usuario (user agent). Podría decirse que es un software que actúa como representante de un usuario (persona, dispositivo…) de cara a comunicarse y facilitar la interacción con un servidor web. Ejemplos de software que trabajan como agentes de usuario serían un navegador web, pero también podría ser un gestor de correo, un simple chat web…
Para la comunicación, se utiliza toda la información posible para que el servidor, una vez le llegue una petición, sepa detectar y distinguir quién es dicho usuario y saber cómo interactuar con él dependiendo de lo que el agente espera.
Esta información va definida en formato cadena (string) y tiene un formato muy parecido a este:
Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1
De un simple vistazo se puede apreciar con qué dispositivo se ha realizado la petición web (un dispositivo móvil, concretamente iPhone), sistema operativo y versión, navegador (Safari) y su compatibilidad (Mozilla).
Para evitar problemas de seguridad, esta comunicación entre agente de usuario (software) y servidor web está limitada a que pertenezcan al mismo dominio. Es decir, si el dominio de trabajo del agente de usuario es “digital55.com” (localhost -al estar alojados en el mismo servidor-) y el del servidor web donde se envía la petición también es “digital55.com” (localhost), la petición pasará el filtro básico de seguridad y este último ya se encargará de tratar o no la respuesta acorde a otros factores (método HTTP correcto, parámetros, etc.).
Un problema bastante común se produce cuando el agente de usuario que envía una solicitud de información al servidor web no pertenece al mismo dominio (es decir, no están alojados en el mismo servidor) o cuando se requiere realizar peticiones a servidores de terceros.
En aplicaciones móviles orientadas a web (por ejemplo, Ionic), se utiliza “localhost” como origen de la petición y si el destino para la petición es “digital55.com”, la comunicación entre agente y servidor se bloqueará por defecto siguiendo las directrices de la política de mismo origen.
Es en este caso donde entra en juego CORS. Es un sistema o mecanismo que permite añadir información extra en la petición para la comunicación con un servidor web de distinto dominio y que así sea posible saltarse esa restricción. Simplemente, para legitimar una petición. Esta información extra se realiza mediante cabeceras HTTP.
Para tener todavía más claro en qué casos podría rechazarse una solicitud de un agente de usuario a un servidor web de distinto dominio, aquí una tabla con algunos ejemplos:
Aplicación móvil que realiza peticiones web y está alojada en http://digital55.com/cliente
Cómo afrontar estos problemas (y algunas soluciones en el desarrollo cliente)
Para afrontar el problema de la política del mismo origen, lo más sencillo siempre será configurar el servidor para admitir qué dominios y qué tipo de peticiones (con qué métodos HTTP, dominios, puertos de origen…) están permitidas. Para ello, existen cabeceras HTTP de respuestas donde le indican a cada agente de usuario que realiza una petición qué es lo que espera recibir.
Las cabeceras más comunes son:
- Access-Control-Allow-Methods: Se definen, separados por comas, los métodos HTTP permitidos. En un servidor web con peticiones HTTP de respuesta lo normal es tener peticiones GET, PUT, POST, DELETE y OPTIONS, es decir, todos los métodos que siguen la arquitectura Restful.
- Access-Control-Allow-Origin: Esta cabecera indica qué dominios de origen permite. Si el origen de las peticiones siempre será desde “localhost”, el servidor web debería tener configurado este valor siempre para que la política del mismo origen no se aplique. También se admite como valor “*” en el caso de que el servidor web reciba peticiones de muchos dominios distintos. En este caso, sería la solución más efectiva porque anularía por completo la política del mismo origen, pero también incrementaría el riesgo de seguridad al permitir que una aplicación cualquiera, alojada en un servidor cualquiera, pueda realizar peticiones sin bloqueo.
Estas cabeceras de respuesta, si son posibles modificarlas desde el servidor web destino, son sin duda la solución ideal ya que el agente de usuario a la hora de realizar una petición HTTP nunca se le presentará el problema de la política del mismo origen. Pero esto no siempre pasa, ya que en algunos casos, en algunos proyectos a los que uno se enfrenta, no siempre se tiene acceso a la configuración del servidor. Es aquí cuando complica el desarrollo de una aplicación web o aplicación móvil cliente y hay que buscar otras soluciones.
Siguiendo con la tecnología de Ionic, en su blog, explican algunos consejos a la hora de intentar paliar estos errores desde el lado de la aplicación cliente.
Dos soluciones rápidas aunque, todo hay que decirlo, no siempre efectivas, son:
- Instalación de extensión para el navegador: Las aplicaciones móviles realizadas con Ionic se desarrollan y se prueban normalmente utilizando el navegador web. Además, al utilizar la consola web y probar distintas resoluciones/dispositivos virtuales, y realizar peticiones a un servidor web externo, simulan el agente de usuario concreto del dispositivo virtual (por ejemplos, si se utiliza un iPhone XR o Samsung Galaxy S10, se genera la información del agente de usuario concreto del dispositivo) además que utiliza por defecto localhost como dominio de origen, por tanto, el dominio siempre será diferente y se aplicará la política del mismo origen. Para solventarlo, existen diferentes plugins o extensiones para el navegador que lo que hacen es introducir en cualquier cabecera de respuesta la cabecera de “Access-Control-Allow-Origin” con el valor “*”. De esta forma, se corrige el problema ya que el cliente siempre estará permitido. Esta solución solo está recomendada para la fase de desarrollo y también si el dominio de destino al que irá la aplicación cliente acabe siendo el mismo que el del servidor web destino.
- Peticiones nativas: En este otro artículo del blog de Ionic se explica cómo funcionan las peticiones HTTP con Ionic. Dependiendo de cómo se pruebe la aplicación y en qué modo se ejecute, las peticiones HTTP pueden estar sujetas a problemas de mismo origen o no.
- Ejecución en el navegador (ionic serve): Al ejecutarse la aplicación en un navegador, cualquier petición HTTP realizada estará sujeta a restricción de política de mismo origen, por tanto, en este caso, se está sujeto a las soluciones anteriormente aportadas.
- Ejecución en el dispositivo con recarga automática (live reload) (ionic cordova run … –livereload): Esta ejecución suele ser interesante cuando se quiere probar un plugin o complemento nativo en el dispositivo, por lo que se ejecuta la aplicación en un dispositivo físico pero se permite la recarga automática si se hace algún cambio en el código de la aplicación, para visualizarlo inmediatamente. Con esta ejecución, también es dependiente de las soluciones anteriores, ya que para permitir una rápida respuesta en la recarga de la aplicación, básicamente es un planchado de código web en el dispositivo, y por tanto, las peticiones se realizan mediante un navegador web interno.
- Ejecución en el dispositivo (ionic cordova run): Con este tipo de ejecución, la aplicación se construye de tal manera que no utilizan el protocolo HTTP para acceder a las páginas de la aplicación, y aunque para acceder a recursos externos HTTP sí están sujetos a las políticas y restricciones, sí existe una forma de saltárselo. Instalando el plugin nativo (solo Android e iOS): ionic-native-http. La ventaja de este plugin es que los errores de CORS quedarán en gran parte solucionados (no es efectivo al 100% porque el servidor web puede tener otras limitaciones de acceso). Esta solución es útil pero también poco práctica porque si todas las peticiones que se realizan en la aplicación dan problemas por el bloqueo de la política del mismo origen, para cada prueba que se requiera realizar en la aplicación en cuestión, hay que construir la aplicación y ejecutarla físicamente en un dispositivo, lo cual es una gran desventaja porque no se estará aprovechando la facilidad de ejecutarlo en un navegador para probar la aplicación.
En resumen, los problemas de la política del mismo origen y la solución que CORS plantea son problemas que están a la orden del día en aplicaciones web/móviles que hacen uso de llamadas a servicios externos. Lo más fácil es comunicarse con los proveedores del servicio o servidor web y habilitar las peticiones para la aplicación web/móvil cliente. Si esto, por cualquier razón, es muy difícil de conseguir existen diferentes soluciones desde el lado del cliente que pueden paliar el problema.