Posterior a consultar expertos en este tema, programadores de varias ramas y profesores dimos con la solución al problema y la plasmamos en esta publicación.
Solución:
xss_clean() es extenso y también tonto. El 90% de esta función no hace nada para evitar xss. como buscar la palabra alert
pero no document.cookie
. Ningún hacker va a usar alert
en su hazaña, secuestrarán la cookie con xss o leerán un token CSRF para hacer un XHR.
Sin embargo corriendo htmlentities()
o htmlspecialchars()
con eso es redundante. Un caso donde xss_clean()
soluciona el problema y htmlentities($text, ENT_COMPAT, 'UTF-8')
falla es el siguiente:
";
?>
Un poc simple es:
http://localhost/xss.php?var=http://dominio/alguna_imagen.gif’%20onload=alert(/xss/)
Esto agregará el onload=
controlador de eventos a la etiqueta de la imagen. Un método para detener esta forma de xss es htmlspecialchars($var,ENT_QUOTES);
o en este caso xss_clean()
también evitará esto.
Sin embargo, citando la documentación de xss_clean():
Nada es 100% infalible, por supuesto, pero no he podido hacer que nada pase el filtro.
Dicho esto, XSS es un output problem
no un input problem
. Por ejemplo, esta función no puede tener en cuenta que la variable ya está dentro de un etiqueta o controlador de eventos. Tampoco detiene XSS basado en DOM. Tienes que tener en cuenta cómo estás usando los datos para utilizar la mejor función. Filtrar todos los datos en la entrada es un mala práctica. No solo es inseguro, sino que también corrompe los datos, lo que puede dificultar las comparaciones.
En su caso, "los métodos más estrictos están bien y son más livianos". Los desarrolladores de CodeIgniter pretenden xss_clean() para un caso de uso diferente, "un sistema de comentarios o foro que permite etiquetas HTML 'seguras'". Esto no está claro en la documentación, donde se muestra xss_clean aplicado a un campo de nombre de usuario.
Hay otra razón para nunca usar xss_clean(), que no se ha resaltado en stackoverflow hasta ahora. xss_clean() se rompió durante 2011 y 2012, y es imposible arreglarlo por completo. Al menos sin un rediseño completo, lo que no sucedió. Por el momento, todavía es vulnerable a cadenas como esta:
Hello
La implementación actual de xss_clean() comienza aplicando efectivamente urldecode() y html_entity_decode() a todo el string. Esto es necesario para que pueda usar una verificación ingenua para cosas como "javascript:". Al final, devuelve lo decodificado string.
Un atacante puede simplemente codificar su hazaña dos veces. Será decodificado una vez por xss_clean(), y pasará como limpio. Entonces tiene un exploit codificado individualmente, listo para ejecutarse en el navegador.
Llamo a estas comprobaciones "ingenuas" e irreparables porque dependen en gran medida de expresiones regulares. HTML no es un lenguaje regular. Necesita un analizador más potente para que coincida con el del navegador; xss_clean() no tiene nada de eso. Tal vez sea posible incluir en la lista blanca un subconjunto de HTML, que lexes limpiamente con expresiones regulares. Sin embargo, el xss_clean() actual es en gran medida una lista negra.
Recomendaría usar http://htmlpurifier.org/ para realizar la purificación XSS. Estoy trabajando para extender mi clase CodeIgniter Input para comenzar a aprovecharla.
Puedes añadir valor a nuestro contenido cooperando tu veteranía en las explicaciones.