style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Ejemplo Cross-Site Scripting

El tercer y último ejemplo que vimos en la plática de Seguridad en Aplicaciones Java, fue el de Cross-Site Scripting, conocido por su abreviatura como XSS.

Este ataque puede ser bastante complejo en cuanto al daño que se pueda hacer y los lugares por donde se puede realizar (vectores de ataque). Sin embargo, el fundamento es muy similar al de la inyección de SQL: usar datos que capturan los usuarios, sin validarlos apropiadamente.

La inyección de SQL permite que un usuario malicioso pueda enviar a una aplicación ciertos datos que incluyan sentencias de SQL que se ejecutarán en el servidor de base de datos. Los ataques de XSS usan ese mismo principio pero los datos que se envian, pueden incluir código HTML y/o Javascript; la principal diferencia es que dicho Javascript es inocuo en el servidor pero al ser incluido en páginas que se envían a otros usuarios, sus navegadores posiblemente ejecutarán dicho Javascript.

¿Cuál puede ser el daño? Pues depende del Javascript que se inyecte en la página, pero a fin de cuentas si el sitio permite que un usuario teclee un texto (por ejemplo un comentario en un foro) y ese texto contiene comandos de javascript y se incluyen tal cual en la página generada ya con ese comentario incluido, se le está dando oportunidad a un atacante de ejecutar programas de Javascript en los navegadores de los otros usuarios que visualizan esa página. Pueden por ejemplo leer todos los cookies del usuario y enviarlos a otro lugar; incluso si el navegador solamente permite que Javascript invoque URL's del mismo sitio, puede ser que el atacante ya tenga algo puesto en sitio (por ejemplo un foro o un perfil de usuario) y la info tomada de los usuarios se envia como comentario o mensaje privado a dicho perfil.

Este ataque no es muy común en aplicaciones hechas en Java, gracias a que la mayoría de los frameworks que se utilizan hoy en día como Struts, JSF, JSTL, Tapestry, etc, tienen ya integrada la protección contra esto, por medio del componente o mecanismo que despliega cadenas obtenidas en tiempo de ejecución, escapando los caracteres especiales como < y > (principalmente) y algunos otros que podrían tener un efecto no deseado en el despliegue de una página en el navegador del usuario.

El ejemplo que mostré en la plática simplemente incluía un campo de texto donde se pueda teclear algo y después se despliega ese texto en la página (el típico "hola mundo" en JSP):

<form action="xss.jsp">
Escribe tu nombre: <input type="text" value="nombre" />
<input type="submit" value="Enviar" />
</form>

<% if (request.getParameter("nombre") != null) { %>
Hola, <%= request.getParameter("nombre") %>
<% } %>

El problema es cuando se teclea algo como <script>alert('Hola!');</script>; si ese texto tal cual se pone en la página generada, el navegador interpretará el Javascript y lo ejecutará; como consecuencia, cuando se teclea el texto y se da enter, en cuanto aparece la página resultante aparece un diálogo de Javascript con el texto "Hola".

En vez de poner ese Javascript tan sencillo se podría incluir un script mucho más largo y complicado que hiciera otras cosas. La idea es que se está poniendo el texto que los usuarios teclean, tal cual, en la página.

Para remediar esto hay distintas soluciones, que dependen de la tecnología. Los frameworks de GUI que están construidos sobre JSP o JSF por lo general incluyen un tag, componente, elemento, alguna sintaxis especial o algo para desplegar cadenas de texto en la página; casi siempre por default escapan el HTML. Por ejemplo, con JSTL si usamos ${} para desplegar el resultado, no se filtra el texto, pero si usamos <c:out> ese elemento por default escapa los caracteres especiales de XML, a menos que se le indique escapeXml="false".

Ejemplo corregido con JSTL:

<form action="xss2.jsp" method="POST">
Escribe tu nombre: <input type="text" name="nombre">
<input type="submit" value="Enviar" />
</form>

<c:if test="${not empty param.nombre }">
Hola, <c:out value="${param.nombre}" />
</c:if>

El ejemplo original tiene una desventaja adicional: la forma no tiene el atributo method y eso causa que por default se use GET, que consiste en incluir los datos de la forma en el URL destino. Esto causa un problema de fuga de información ya que los datos viajan en el URL y se quedan en el log del web server, son visibles en la barra de dirección del navegador, pasan tal cual por el proxy quedando en su log, etc. El segundo ejemplo usa POST, que envía los datos de la forma después del URL y los encabezados de HTTP, por lo que no son visibles en el URL, evitando estos problemas (aunque siguen viajando sin protección alguna, pero es una protección de lo más básico).

En Struts 2, el tag <s:property> también ya escapa el HTML en el texto que se le pase, a menos que se le ponga escape="false".

En Tapestry 3 y 4 el componente Insert escapa el HTML del texto que despliega, a menos que se le indique raw="true". En Tapestry 5, el componente Output funciona igual, es decir escapa el HTML por default a menos que se le indique filter="false".

El tag <h:outputText> de JSF por default escapa el HTML a menos que se le indique lo contrario con escape="false".

Sin embargo no deja de ser importante probar en sus aplicaciones web que no se pueda inyectar código HTML directamente. Todos estos frameworks que acabo de mencionar, filtran el XML/HTML al momento de desplegarlo, sin embargo no filtran los datos que se capturan. Por lo tanto si un usuario inyecta código HTML, XML o Javascript en algún campo de texto, se puede almacenar en la base de datos. Al tenerlo ahí guardado puede ser una bomba de tiempo porque lo único que se necesita es que en el futuro alguien desarrolle una página donde por descuido se despliegue un texto que venga de la base de datos y que contenga HTML/XML/JS para habilitar un ataque de XSS. En algunos casos es mejor analizar el texto que los usuarios capturan y filtrarlo de una vez, escapando los caracteres que puedan causar problemas y almacenar así la información en la base de datos. Esto no siempre es posible o factible, pero es la mejor manera de evitar estos ataques.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de jmanuel_ll

Excelente...

Excelente artículo, de hecho administro una aplicación que tenía la vulnerabilidad XSS por la versión del Tomcat en que residía (4.1.31) y nos aconsejaron realizar el upgrade a la 4.1.39 (no es mucho avance, pero se corrigió en teoría la vulenrabilidad).

Mi pregunta es la siguiente: ¿Cómo puedo validar que se erradicó la vulnerabilidad XSS?

Saludos,
jm

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">