Seguridad de aplicaciones JavaScript: top 10 vulnerabilidades para principiantes
La seguridad de las aplicaciones web es uno de los temas más importantes para cualquier desarrollador. JavaScript (especialmente en el lado del servidor con Node.js) a menudo se convierte en objetivo de atacantes debido a su popularidad. En este artículo analizaremos las 10 vulnerabilidades más comunes que enfrentan los desarrolladores principiantes y mostraremos cómo evitarlas.
1. XSS (Cross-Site Scripting)
XSS es uno de los ataques más frecuentes, donde un atacante inyecta un script malicioso en una página web. Esto ocurre cuando los datos del usuario se insertan en HTML sin escape.
Código peligroso (vulnerable):
app.get('/search', (req, res) => { const query = req.query.q; res.send(`<h1>Resultados de búsqueda: ${query}</h1>`);});Código seguro (protegido):
const escapeHtml = require('escape-html');
app.get('/search', (req, res) => { const query = escapeHtml(req.query.q); res.send(`<h1>Resultados de búsqueda: ${query}</h1>`);});2. Inyección SQL
Si usas bases de datos SQL (por ejemplo, SQLite o PostgreSQL), las consultas sin escape pueden provocar fugas de datos.
Código peligroso:
const user = req.body.username;db.query(`SELECT * FROM users WHERE name = '${user}'`);Código seguro (usamos consultas parametrizadas):
const user = req.body.username;db.query('SELECT * FROM users WHERE name = $1', [user]);3. Deserialización insegura
Usar JSON.parse() en datos no verificados puede provocar ataques de tipo "prototype pollution".
Ejemplo de vulnerabilidad:
const data = JSON.parse(req.body.jsonData);// Si data contiene __proto__, esto puede cambiar el prototipo de ObjectProtección: siempre valida los datos antes de parsearlos o usa bibliotecas como fast-json-stable-stringify.
4. Falta de validación de entrada (Input Validation)
Nunca confíes en los datos del usuario. Siempre verifica el tipo, la longitud y el formato de los datos.
// Mal: aceptamos cualquier cosaapp.post('/profile', (req, res) => { const { name, age } = req.body; // ...});
// Bien: validamosconst schema = { name: 'string', age: 'number' };if (typeof req.body.name !== 'string') throw new Error('Invalid name');5. CSRF (Cross-Site Request Forgery)
Ataque en el que un atacante obliga al usuario a realizar una acción no deseada en un sitio donde está autenticado.
Protección: usa tokens CSRF.
const csrf = require('csurf');app.use(csrf({ cookie: true }));
app.get('/form', (req, res) => { res.render('form', { csrfToken: req.csrfToken() });});6. Fuga de información a través de errores
Nunca muestres al usuario mensajes de error detallados: esto proporciona a los hackers información sobre la estructura de tu aplicación.
Mal:
app.use((err, req, res, next) => { res.status(500).send(err.stack); // ¡Fuga del stack de llamadas!});Bien:
app.use((err, req, res, next) => { console.error(err.stack); // Registramos para nosotros res.status(500).send('Error interno del servidor');});7. Uso inseguro de eval() y Function()
Las funciones eval() y new Function() ejecutan una cadena como código — esto es extremadamente peligroso si la cadena contiene datos del usuario.
// ¡Peligroso! Nunca hagas estoconst userInput = req.body.code;eval(userInput); // Un atacante puede ejecutar cualquier códigoAlternativa: