Previniendo ataques XSS (I/II)

¿De qué va este post?

En este post explicaré cómo prevenir los ataques de Cross Site Scripting (o XSS)
de forma muy sencilla, pero efectiva.

¿Por qué?

Porque, tras realizar una auditoría de seguridad sobre un entorno web, y recomendar a los desarrolladores que implementen un filtrado correcto nos hemos dado cuenta que:
  1. Muchos no saben cómo hacerlo correctamente.
  2. Implementan filtros que no son los correctos y se pueden seguir eludiendo.
  3. Piensan que la implementación de esos filtros es muy compleja, cuando no es así.

¿Cuál es el problema?

No voy a explicar en qué consiste un ataque XSS (google contestará a esta pregunta sobradamente) pero es bien conocido el perjuicio tan grave que supone para los usuarios.
El problema radica en unas malas prácticas a la hora de desarrollar un sitio web y no se filtra correctamente toda la información que se va a mostrar al usuario.

Se pueden diferenciar 2 planteamientos:
1 – Cuando se utiliza un framework de programación (spring, struts, ASP.net, symfony, cakePHP, Django…), que hace más complicado ser vulnerable a este tipo de ataques, salvo aquellos casos en los que se utiliza o configura correctamente el framework utilizado. La mayoría permiten configurar de forma muy rigurosa los tipos de datos de entrada y éstos se encargan de filtrar posibles ataques. Pero no todos los desarrolladores conocen el poder de éstos y desaprovechan su potencial.
2 – Cuando se desarrolla desde cero, estos filtros tienen que ser creados manualmente y tener mucho cuidado con todos los valores de entrada. En la práctica no se suele hacer bien por desconocimiento de la vulnerabilidad o bien por no saber cómo hacerlo.
El problema se complica más aun cuando es necesario permitir al usuario introducir etiquetas HTML, como pueden ser los comentarios de un foro.

¿Qué tipos de filtros existen?

Podemos implementar un sistema de filtrado sencillo en función de las necesidades que tengamos:
  1. Todo lo que introduce el usuario ha de ser filtrado. No permitimos ningún carácter extraño.
  2. Debemos permitir al usuario que introduzca valores HTML simples como: negrita (), cursiva (), salto de línea (
    )…
  3. Debemos permitir al usuario que introduzca valores HTML que aceptan complejos: enlaces (), modificación del tipo de letra ()

Implementando filtrado simple

Vamos a implementar un sistema de filtrado atendiendo a los supuestos 1 y 2 del apartado anterior. Lo primero que debemos de tener en la cabeza es que jamás podemos fiarnos de un usuario y lo que éste está dispuesto a hacer. Por esta razón debemos de ponernos siempre en el peor de los casos, aunque parezca algo paranoico.
Como regla general a aplicar deberíamos de seguir la política: denegar todo y permitir solamente aquellos casos concretos que necesitemos. Vamos a la práctica:

Caso 1: Filtrando todo el contenido de salida

Siempre que necesitemos mostrar en el navegador información no estática, variables o información que provenga de una base de datos, debemos de filtrarla. Para ello solamente tendremos que sustituir los caracteres considerados como peligrosos por sus equivalentes HTML:
  1. &  –>  &
  2. <  –>  <
  3. >   –>  >
  4. ”  –>  “
  5. ‘  –>  ‘
  6. /  –>  /
Este supuesto solo es válido si la información se va a mostrar en el cuerpo HTML, NO en el código JAVASCRIPT o CSS. En estos casos hay que prestar atención a otros supuestos adicionales, que dejamos para otro post.

La implementación de este filtro es sumamente simple. A continuación unos ejemplos de cómo implementarlo. Aunque no son los más elegantes ni eficientes, sí son los más simples de entender:
Java


PHP



C#



Python



Caso 2: Permitiendo tags HTML simples

Hay ocasiones que necesitamos que el usuario pueda añadir ciertos tags HTML simples y permitir que sean interpretados.
De forma errónea muchas veces se tiende a hacer enormes listas de tags HTML considerado peligrosos (esta técnica se conoce como lista negra), intentando dejar solamente los que se desea permitir al usuario. Pero, ¿por qué es esto un error?
Porque como programadores estamos esperando que los usuario introduzcan cosas como: “