Programación (in)segura: XSS

Durante la fase de desarrollo de una aplicación, muchas veces no se aplican los requisitos de seguridad correspondientes y en muchos casos, esto viene provocado por el pobre enfoque de seguridad que tienen los desarrolladores. La seguridad no sólo consiste en validar que los datos recibidos tienen el formato correcto, tamaño correcto, no vienen vacíos o son nulos. Es algo más. En este artículo vamos a ver ejemplos de validación Javascript y ejemplos de validación de datos en servidor.

LA VALIDACIÓN JAVASCRIPT

El poco conocimiento de seguridad de muchos desarrolladores hace que se confíe en la validación de los campos en el lado del cliente, es decir, a través de Javascript. Estas funciones son fácilmente manipulables editando el código fuente de la página y modificándolo. Así, todos los campos que esperábamos que llegasen con valores bien formados, pueden llegar vacíos, con una longitud grande o no llegar. Y si nuestra validación en la parte servidora no es lo suficientemente robusta, se pueden producir fallos de seguridad. Por eso, no hay que confiar nunca en los datos enviados por el cliente y considerar la validación con Javascript un complemento de la validación en el servidor.

En el código fuente de la página siguiente se observa un formulario con un evento onSubmit(“return validaAcceso()”) que será invocado cuando el usuario rellene los campos necesarios y pulse el botón Aceptar. La lógica implementada comprobará que los campos DNI y Contraseña estén rellenos y que el DNI sea numérico:

 

Accediendo al código fuente de la página, editaremos el contenido de la función validaAcceso() y eliminaremos todas las comprobaciones, dejando sólo un alert(“Javascript eliminado”) que mostrará el mensaje en pantalla y un return false; para no enviar el formulario al hacer la prueba:

Si se pulsa ahora el botón de Aceptar, sin haber rellenado los datos obligatorios, se muestra el mensaje “Javascript eliminado“, ya que no hay validación, y se devuelve un false. Hemos manipulado toda la lógica implementada en Javascript:

Si en el lado del servidor no hubiese implementada una correcta validación de los campos, tendríamos un problema de seguridad.

VALIDACIÓN, SANITIZACIÓN, CODIFICACIÓN

La validación de los datos de entrada es el mecanismo de defensa más importante del que depende una aplicación web y que está orientado a evitar la entrada de datos inseguros. Una gran cantidad de vulnerabilidades identificadas en una aplicación son debidas a una mala validación de parámetros.

Cada parámetro recibido, aparte de las validaciones comunes, necesitará una validación específica. Es decir, un campo Nombre donde se permiten sólo caracteres de la a-z y A-Z y acentos, no necesita la misma validación que un campo Código Postal, que sólo permite introducir dígitos del 0-9.

Para ver un ejemplo de validación y sanitización de datos recibidos, he escrito en PHP el siguiente ejemplo muy básico, el cual simula un comentario enviado desde un formulario. A la izquierda se observa el código y a la derecha el resultado:

A continuación se detallan los pasos que se han seguido mediante los números marcados en rojo:

  1. Validación: se valida que el parámetro comentario esté definido, no sea vacío y no sea nulo.
  2. Validación: se valida que la longitud del parámetro no sea mayor que 250 caracteres.
  3. Sanitización:
    1. Se eliminan espacios en blanco por delante y por detrás con trim().
    2. Se reemplazan los caracteres especiales < > ” ‘ & por sus entidades HTML correspondientes &lt; &gt; &quot; &#39; &amp; con htmlspecialchars(). Esta función es recomendable usarla antes de enviar los datos al cliente.

De esta forma, si accedemos al código de la página, se observan los caracteres especiales codificados, tal y como se ven marcados en rojo :

Pero entonces, ¿que pasaría si no realizasemos la validación?. Pues probemos a ver 😀 . Comentamos la línea que contiene la función htmlspecialchars() y ejecutamos para ver el resultado:

Al no utilizarse la función htmlspecialchars() a la hora de enviar datos, el código ejecutable <script>alert(document.cookie);</script> del comentario es ejecutado por el navegador.

EJEMPLOS DE XSS PERSISTENTE

Vamos a mostrar dos ejemplos de inyección:

EJEMPLO 1:

Un usuario se autentica en una aplicación y accede a su perfil para realizar algunos cambios. En uno de los campos, que sirve para poner un texto, en vez de introducir un texto introduce “><img src=”x” onerror=”alert(document.cookie)”> :

Este parámetro, al no existir una validación del mismo, se inserta en base de datos. Cuando se acceda a una página en la que se requiera del valor de este campo, se recuperará su valor de base de datos y, sin codificarlo, se envia al navegador el cual ejecutará el código :

EJEMPLO 2:

Un usuario, en una aplicación desarrollada con AngularJS, modifica el campo address en el que, en vez de una dirección válida, inserta el valor [[constructor.constructor(‘alert(document.cookie)’)()]] :

Al igual que el ejemplo anterior, una vez guardados los datos y se acceda a una página que requiera el campo address, se recuperará su valor de base de datos y, sin codificarlo, se enviará al navegador que identificará el código y lo ejecutará, mostrando la cookie de sesión del usuario:

Una correcta validación de los datos de entrada y codificación de los datos de salida enviarían al navegador el texto [[constructor.constructor(&#039;alert(document.cookie)&#039;)()]] imprimible pero no ejecutable.

Si observamos este flujo de forma gráfica, se ve como la petición realizada desde el Navegador cliente (línea azul) envía el parámetro address con el valor [[constructor.constructor(‘alert(document.cookie)’)()]] (etiqueta azul – Petición) y se inserta en base de datos sin ser validado ni sanitizado. Cuando la aplicación requiera de ese dato, se recupera el registro (línea roja), pero al no codificar los datos devueltos (etiqueta amarilla – Respuesta) el navegador identificará y ejecutará el código, obteniendo la cookie de sesión del usuario:

Con esto se pretende hacer ver que es necesario realizar siempre una validación (verificar tipo de dato, longitud, que no sea vacío, que no sea nulo, etc…) y sanitización (eliminar los caracteres especiales que puedan dar problemas) de los datos de entrada desde el cliente en un entorno seguro como es el servidor y no en un entorno inseguro como es el navegador

 

Por lo tanto, en función de los campos que se vayan a tratar y de los datos que se vayan a mostrar, se deberán utilizar unas validaciones u otras pero… siempre validar !!!

Próximamente veremos la parte de codificación de los datos de salida.

Esperamos que os haya gustado.

Don’t give up, great things take time.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *