Seguridad de aplicaciones JavaScript: top 10 vulnerabilidades
JavaScript, especialmente en el entorno Node.js, se ha convertido en la base del desarrollo web moderno. Sin embargo, su popularidad hace que las aplicaciones sean un objetivo atractivo para los ataques. Ignorar los problemas de seguridad puede provocar fugas de datos, pérdidas financieras y daños a la reputación. En este artículo, analizaremos las 10 vulnerabilidades más críticas de las aplicaciones JavaScript (Node.js), basadas en el estándar OWASP Top 10, y mostraremos cómo prevenirlas con ejemplos de código.
1. Inyecciones (Injection)
Las inyecciones siguen siendo una de las amenazas más peligrosas. En el contexto de Node.js, las más comunes son las SQL injection y las NoSQL injection (por ejemplo, en MongoDB).
Problema
La concatenación directa de la entrada del usuario en las consultas a la base de datos permite a un atacante ejecutar código arbitrario.
Ejemplo de código vulnerable
const user = req.body.username;const query = `SELECT * FROM users WHERE username = '${user}'`;db.query(query, callback);Solución
Utilice consultas parametrizadas u ORM/ODM. Para MongoDB, use mongo-sanitize.
const query = 'SELECT * FROM users WHERE username = $1';db.query(query, [user], callback);2. Fallos de autenticación y gestión de sesiones
Una autenticación débil es un camino directo al compromiso de cuentas.
Problema
Uso de tokens predecibles (por ejemplo, JWT con el algoritmo none), almacenamiento de contraseñas en texto plano o falta de limitación en el número de intentos de inicio de sesión.
Ejemplo de vulnerabilidad
// JWT sin verificación de algoritmoconst token = jwt.sign({ user: 'admin' }, 'secret', { algorithm: 'none' });Solución
- Siempre verifique el algoritmo JWT:
jwt.verify(token, secret, { algorithms: ['HS256'] }). - Almacene las contraseñas usando
bcryptoargon2. - Implemente un límite de intentos de inicio de sesión (
express-rate-limit).
const bcrypt = require('bcrypt');const saltRounds = 10;const hash = await bcrypt.hash(password, saltRounds);3. XSS (Cross-Site Scripting)
XSS permite inyectar un script malicioso en una página web, que luego se ejecuta en el navegador de la víctima.
Problema
Mostrar contenido del usuario sin escapar, especialmente al usar Server-Side Rendering (SSR) en Next.js o Nuxt.js.
Código vulnerable (Express)
res.send(`<h1>Hello, ${req.query.name}</h1>`);Solución
- Escape la salida: use bibliotecas como
escape-htmlo motores de plantillas con auto-escaped (EJS, Pug). - Establezca la cabecera CSP (Content Security Policy):
helmet.contentSecurityPolicy().
const helmet = require('helmet');app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"] }}));4. Referencia directa insegura a objetos (IDOR)
La vulnerabilidad surge cuando un usuario puede acceder a objetos (archivos, registros de BD) conociendo o adivinando su identificador.
Ejemplo
app.get('/api/user/:id', (req, res) => { // No hay verificación de que req.user.id === req.params.id const user = db.findUser(req.params.id); res.json(user);});Solución
Siempre verifique los permisos de acceso a nivel del servidor.
if (req.user.id !== req.params.id) { return res.status(403).send('Forbidden');}5. Configuración de seguridad insegura
Esto incluye el uso de credenciales predeterminadas, modos de depuración en producción y configuraciones incorrectas de CORS.
Problema
// CORS abiertoapp.use(cors({ origin: '*' }));