Codificar los datos para evitar ataques XSS en una página web

Escrito por el .
planeta-codigo programacion
Comentarios

HTML
Java

Validar los datos es importante para una aplicación pero no es suficiente para crear una aplicación, es más, para crear una aplicación segura es más importante codificar correctamente los datos emitidos por la aplicación. Los ataques XSS se producen precisamente por no codificar correctamente los datos emitidos provenientes de una fuente no confiable. Una fuente no confiable puede ser un parámetro en una aplicación web pero también puede ser cualquier otro dato que incluya una petición HTTP como una cabecera.

El siguiente código de un archivo JSP que obtiene un parámetro de la petición y lo emite en la salida permite a un usuario malicioso insertar código en la página web, si el dato que se envía es el contenido de xss.data y las cookies no se crearon con las cabeceras httponly el usuario malicioso puede obtener acceso a la sesión del usuario en el sitio web y es un grave fallo de seguridad. En este caso solo se emplea un alert pero el código podría ser más elaborado y realizar una petición a una URL en la que el usuario malicioso reciba los datos de las cookies de sesión.

1
2
3
4
5
6
7
8
<html>
<head>
    <title>Página</title>
</head>
<body>
    Hola <%= request.getParameter("user"); %>!
</body>
</html>
1
2
Hacker <script type="text/javascript">alert("Hacked!"); alert(document.cookie);</script>

El contenido HTML generado por la aplicación y enviado al navegador sería el siguiente:

1
2
3
4
5
6
7
8
<html>
<head>
    <title>Página</title>
</head>
<body>
    Hola Hacker <script type="text/javascript">alert("Hacked!"); alert(document.cookie);</script>!
</body>
</html>

En este caso al cargar la página en el navegador se muestra un mensaje alert con una ventana emergente pero si el usuario malicioso enviase los datos de las cookies a una URL suya el usuario ni siquiera sería consciente de que le han robado la sesión. Y este fallo de seguridad se produce simplemente por cargar una página de una aplicación insegura por XSS.

XSS

Pero ¿como consigue el usuario malicioso inyectar su código mediante parámetros u otros datos emitidos por la página insegura? Una opción sería enviar al usuario un enlace especialmente construido para que se aproveche del fallo de seguridad, el medio de hacerlo llegar puede ser un correo electrónico o un enlace en las redes sociales o páginas de gran tráfico como Facebook. Para que el enlace no sea tan evidente se puede utilizar un acortador de enlaces. Los comentarios son otro vector con el que el usuario malicioso puede insertar enlaces o el propio contenido si no son tratados adecuadamente donde sean mostrados como en la página web, de otros médios como correos electrónicos o una aplicación de backoffice de uso interno que incluso puede tener privilegios de realizar acciones especialmente sensibles.

Para evitar los ataques XSS la regla básica es codificar correctamente cualquier dato que se emita como resultado. En el caso de una aplicación web codificar correctamente el dato a emitir depende del contexto donde se incluya el dato para escapar correctamente los caracteres especiales de ese contexto. Los contextos puede ser como HTML, como un atributo de una etiqueta, como contenido HTML, como un bloque de JavaScript o como una variable de JavaScript.

Si el framework web que usamos no proporciona facilidades para evitar ataques XSS al emitir contenido en el resultado en Java se puede usar la librería OWASP Java Encoder Project siendo su uso el siguiente. OWASP Java Encoder Project es una librería que no tiene dependencias adicionales por lo que es sencillo incorporarla en la aplicación.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page import="org.owasp.encoder.Encoder" %>

<%-- HTML Content context --%>
<body>
    <%= Encode.forHtml(text) %>
</body>

<%-- HTML Attribute context --%>
<input type="text" name="address" value="<%= Encode.forHtmlAttribute(address) %>" />

<%-- HTML Textarea context --%>
<textarea name="text"><%= Encode.forHtmlContent(textAreaContent %></textarea>

<%-- Javascript Block context --%>
<script type="text/javascript">
    var msg = "<%= Encode.forJavaScriptBlock(message) %>";
    alert(msg);
</script>

<%-- Javascript Variable context --%>
<button onclick="alert('<%= Encode.forJavaScriptAttribute(alertMsg) %>');">Action</button>

Para los frameworks web populares ya tienen en cuenta el XSS en el comportamiento por defecto al emitir datos.

No siempre se desea codificar un dato simple según el contexto, a veces se quiere permitir insertar código HTML. La alternativa a la codificación para estos casos es el saneado permitiendo únicamente los elementos que se consideren seguros definiendo una política, por ejemplo, permitiendo etiquetas HTML de enlaces y ciertas etiquetas como em, ul, li. El saneado también se puede hacer con jsoup pero esta incluye otras funcionalidades que si no son necesarias con WASP Saniticer es suficiente.

Un ataque de XSS también puede producirse en contenidos distintos al HTML como pueden ser documentos JSON o XML en los que también hay que escapar los datos según su contexto para evitar por ejemplo que alguien cambien la estructura del XML o JSON previsto por la aplicación. El XSS es similar al problema de inyección de SQL si al construir las sentencias se insertan en ella datos en vez de usar en el caso de Java la clase PreparedStatement con parámetros.