Articles

CORS, XSS y CSRF con ejemplos en 10 minutos

Este artículo debería ser tu punto de entrada para los estándares de seguridad web existentes, los ataques web más comunes y los métodos para prevenirlos. Al final, también descubrirás cómo y por qué Samy era el héroe de todo el mundo.(excepto de Rupert Murdoch, supongo)

Cross-origin resource sharing, o CORS, es la característica de seguridad de IE10+, Chrome 4+, Firefox 3.5+ o casi todas las versiones de navegadores lanzadas después de 2012 excepto Opera Mini.

Cuando se configura CORS en un servidor que está disponible en el dominio website.com entonces los recursos de ese dominio que se solicitan a través de AJAX deben iniciarse desde los activos que se sirven desde ese mismo dominio.
CORS

En otras palabras, si habilitamos CORS en domain-b.com y lo configuramos para permitir sólo GET peticiones desde el dominio domain-b.com entonces si usted quiere utilizar la imagen disponible bajo https://domain-b.com/images/example.png en el lienzo en su sitio web que está alojado en domain-a.com, que esa imagen no se cargará para la mayoría de sus visitantes.
Sus recursos protegidos por CORS seguirán estando disponibles cuando sean solicitados por cualquier herramienta o navegador que no respete CORS policy.

Configuración de CORS

Los CORS están deshabilitados por defecto lo que significa que no hay un manejador de servidor adecuado que configure los CORS lo que significa que no puede acceder a recursos de diferente origen en su XHR. Básicamente, si no haces nada o habilitas específicamente CORS sólo para dominios específicos, entonces cualquier petición AJAX que intente acceder a tus recursos será rechazada porque los navegadores web son respetuosos con el CORS policy.
Esta es la razón por la que te encuentras con el problema de CORS cuando empiezas a desarrollar SPA usando VueJS y NodeJS. Tu aplicación VueJS está alojada en http://localhost:8080 y cuando intentas acceder a la aplicación del servidor NodeJS en http://localhost:8000 obtienes «No Access-Control-Allow-Origin header is present» porque esos son dos ORIGINS diferentes (combinación de PROTOCOL, HOST y PORT).

Una buena solución para el problema de CORS en el modo de desarrollo de VueJS es establecer el proxy devServer en su archivo vue.config.js de la siguiente manera:

module.exports = { ... devServer: { proxy: 'http://localhost:8000', }, ...}
Entrar en el modo de pantalla completa Salir del modo de pantalla completa

Para configurar CORS en la producción debe agregar el oyente apropiado para OPTIONS solicitud. Ese listener debe enviar la respuesta 200 con no body pero con Headers que definirá su política CORS deseada:

Access-Control-Allow-Origin: https://domain-b.comAccess-Control-Allow-Methods: GET
Entrar en modo de pantalla completa Salir del modo de pantalla completa

Para más información sobre cómo configurar CORS revisa https://enable-cors.org/index.html, y para profundizar en CORS policyrevisa https://livebook.manning.com/book/cors-in-action/part-1/

XSS

XSS significa Cross Site Scripting y es un tipo de ataque de inyección. Aparece como la séptima de las 10 principales vulnerabilidades identificadas por OWASP en 2017. Cross site scripting es el método en el que el atacante inyecta un script malicioso en un sitio web de confianza.(sección actualizada, gracias Sandor) Hay 3 tipos de ataques de este tipo.

  1. Stored XSS – Vulnerabilidad proveniente de entradas de usuario desprotegidas y no saneadas que se almacenan directamente en la base de datos y se muestran a otros usuarios
  2. Reflected XSS – Vulnerabilidad proveniente de valores desprotegidos y no saneados de URLs que se utilizan directamente en páginas web
  3. DOM based XSS – Similar a reflected XSS, valores desprotegidos y no saneados de URLs que se utilizan directamente en las páginas web, con la diferencia de que el XSS basado en DOM ni siquiera va al lado del servidor

Ataque

1. XSS almacenado

Este es un ejemplo de ataque. El atacante entra en su sitio web y encuentra un campo de entrada desprotegido, como el campo de comentarios o el campo de nombre de usuario, e introduce un script malicioso en lugar del valor esperado. Después de eso, cada vez que ese valor debe ser mostrado a otros usuarios se ejecutará el código malicioso. El script malicioso puede tratar de acceder a su cuenta en otros sitios web, puede ser el involucrado en el ataque DDoS o similares. Representación visual(fuente geeksforgeeks.org):

XSS example

2. XSS reflejado

El XSS reflejado es un ataque que se produce cuando el atacante descubre la página con dicha vulnerabilidad, por ejemplo:

URL esperada: https://mywebpage.com/search?q=javascript
URL maliciosa: https://mywebpage.com/search?q=<script>alert('fortunately, this will not work!')</script>

<body>...<div> showing results for keyword <script> document.write(window.location.href.substr(window.location.href.indexOf('q=') + 2))</script></div>......JavaScript results......</body>
Entrar en modo de pantalla completa Salir de modo de pantalla completa

Después del descubrimiento, el atacante atrae al usuario para que haga clic en dicha URL maliciosa y voilá. Los datos sensibles del usuario son explotados.

Ciclo de vida del ataque ilustrado en el ejemplo proporcionado por geekforgeeks.com:

Reflected XSS example

3. XSS basado en DOM

Este tipo de ataque es el mismo que el reflejado pero con la diferencia de que la parte maliciosa URL no será enviada al servidor en absoluto. Para el ejemplo anterior:

URL esperada: https://mywebpage.com/search?q=javascript
URL maliciosa (XSS reflejado): https://mywebpage.com/search?q=<script>alert('fortunately, this will not work!')</script>
URL maliciosa (XSS basado en DOM): https://mywebpage.com/search#q=<script>alert('fortunately, this will not work!')</script>

La diferencia está en que se utiliza el carácter # en lugar de ?. Los navegadores no envían parte de la URL después de # al servidor por lo que la pasan directamente a su código de cliente.

Protección

¡Todo valor que pueda ser introducido por el usuario y que se utilice en su aplicación (ya sea en el lado del servidor o en el lado del cliente) debe ser tratado como datos no confiables y por lo tanto debe ser procesado antes de usarlo! Como se muestra en la documentación, VueJS por sí mismo escapa la cadena antes de obtener el valor de la variable. Las nuevas versiones de Angular también escapan cadenas implícitamente, por lo que si usted está usando Vanilla JS, JQuery o similar debe implementar el escape de cadenas manualmente.

Hay tres enfoques más comunes en el procesamiento de datos no confiables se enumeran a continuación y el método ideal depende del tipo real del campo que usted necesita para procesar.

1. Validación de cadenas

La validación es el método en el que el usuario define un conjunto de reglas, y exige que los datos no confiables satisfagan esas reglas antes de continuar. Este método es bueno para valores numéricos, nombre de usuario, correo electrónico, contraseña y campos similares con un conjunto concreto de reglas de sintaxis.

Comprueba las bibliotecas existentes para tu framework antes de considerar escribir validadores por tu cuenta.

2. String escape

El método escape es útil para los casos en los que se debe permitir al usuario el uso de signos de puntuación. Este método recorre la cadena y busca los caracteres especiales, como < > y los reemplaza con el nombre de entidad de caracteres HTML apropiado. Aquí está la función básica que podría utilizar:

function escapeText(text) { return text.replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;')}
Entrar en el modo de pantalla completa Salir del modo de pantalla completa

De nuevo, compruebe las bibliotecas existentes antes de escribir las suyas propias.

3. Sanitización de cadenas

La sanitización de cadenas se utiliza cuando se permite al usuario introducir algunas etiquetas HTML en sus comentarios, artículos o similares. El método de sanitización recorre el texto y busca las etiquetas HTML que se especifican y las elimina. Una de las bibliotecas más populares que utiliza este enfoque es Google Closure.
Este método es costoso en recursos y se considera como dañino, así que haz más investigación antes de elegirlo.

Los navegadores web (no hay fuentes disponibles desde qué versión, IE solucionó este problema en 2014.) escapan automáticamente las URLs antes de enviarlas al lado del servidor y hacerlas disponibles en el objeto window.location también, por lo que el segundo y tercer tipo de ataque están aquí sólo para ser conscientes de ellos y para dejar claro que los params de la URL también deben ser tratados como datos no confiables.

Para información más detallada sobre XSS y cómo proteger adecuadamente tu aplicación si rotas muchos datos no confiables, por favor revisa la hoja de trucos de OWASP sobre prevención de XSS.

CSRF

La falsificación de petición entre sitios o CSRF es un tipo de ataque que se produce cuando un sitio web, correo electrónico, blog, mensaje instantáneo o programa malicioso hace que el navegador web de un usuario realice una acción no deseada en otro sitio de confianza donde el usuario está autenticado. Esta vulnerabilidad es posible cuando el navegador envía automáticamente el recurso de autorización, como la cookie de sesión, la dirección IP o similar con cada solicitud.

ATAQUE

Supongamos que el usuario está conectado a su aplicación web de bolsa desprotegida y que está utilizando la cookie de sesión o la cookie JWT para la autenticación. El atacante también utiliza su servicio y es capaz de comprobar cómo funciona su API. El atacante engaña al usuario para que ejecute un script (haciendo clic en un enlace de SPAM en el correo electrónico o similar) que enviará una solicitud a su API https://www.stockexchange.com/users/withdraw?how_much=all&address=MY_ADDRESS (terrible diseño de API, no pregunte). Dado que la solicitud se ejecuta desde el navegador que envía la carga útil de autenticación con cada solicitud, su servidor web de stockexchange autenticará al usuario con éxito y ejecutará la transacción y el usuario engañado perderá todo su saldo sin siquiera ser consciente de ello porque todo sucedió en el fondo. Representación visual(fuente miro.medium.com)
CSRF attack

PROTECCIÓN

Por suerte existen patrones fáciles de implementar que evitan estos ataques web. Uno de los patrones más comunes es el uso de CSRF token. Básicamente el procedimiento es el siguiente:

  1. Generar un token único para cada petición del usuario, llamado CSRF token.
  2. Almacenarla de forma segura en el servidor y enviarla de vuelta al usuario como payload de la respuesta.
  3. Almacenar CSRF token en el lado del cliente.
  4. Cuando el usuario intenta ejecutar cualquier solicitud de cambio de estado* envía ese CSRF token con la solicitud como carga útil.
  5. Antes de ejecutar esa solicitud en el lado del servidor comprueba si CSRF token está presente y es válido.

Esta es la forma más fácil de prevenir el ataque CSRF para todos los usuarios.

Si usted está tratando sólo con los visitantes que utilizan los navegadores modernos, entonces usted puede confiar en el atributo SameSite de la cookie de sesión. (gracias Gergely)

Dado que las respuestas del servidor son procesables en la respuesta XHR, entonces no hay protección en el ataque CSRF si su aplicación web es vulnerable XSS!

Para profundizar en el tema, consulta la hoja de trucos de OWASP sobre CSRF.

BONUS

Corto documental sobre Samy, autor del gusano que derribó MySpace en 2005 abusando de la vulnerabilidad XSS, pasando la defensa CSRF de MySpace.
https://youtu.be/DtnuaHl378M

Más información sobre el gusano de Samy
https://samy.pl/myspace/tech.html