Seguridad de aplicaciones TypeScript: top 10 vulnerabilidades en la práctica
TypeScript es una extensión de JavaScript que añade tipado estático. Muchos desarrolladores creen erróneamente que el tipado estricto hace que la aplicación sea segura automáticamente. Esto no es así. El compilador de TypeScript verifica tipos, pero no protege contra errores lógicos, inyecciones o problemas de autenticación. En este artículo analizaremos las 10 vulnerabilidades más comunes en aplicaciones TypeScript, mostraremos ejemplos de código y daremos recomendaciones concretas para la protección. El artículo será útil tanto para desarrolladores principiantes como experimentados.
1. Cross-Site Scripting (XSS) mediante procesamiento incorrecto de entrada
XSS sigue siendo una de las vulnerabilidades más peligrosas. TypeScript no escapa la salida automáticamente. Si inserta datos de usuario en una plantilla HTML sin sanitización, está abriendo la puerta a ataques.
Código vulnerable
function renderComment(comment: string): string { return `<div>${comment}</div>`;}
// El atacante envía: <script>alert('XSS')</script>Código corregido
import { escape } from 'lodash';
function renderComment(comment: string): string { const safeComment = escape(comment); return `<div>${safeComment}</div>`;}Recomendación: Utilice siempre bibliotecas para escapar (DOMPurify, lodash.escape) o motores de plantillas con escape automático (React JSX, Handlebars).
2. Inyecciones SQL mediante plantillas de cadenas
TypeScript permite usar plantillas de cadenas para construir consultas SQL. Este es un camino directo a la inyección SQL si inserta datos de usuario.
Código vulnerable
async function getUser(id: string): Promise<User | null> { const query = `SELECT * FROM users WHERE id = '${id}'`; const result = await db.query(query); return result.rows[0];}Código corregido
async function getUser(id: string): Promise<User | null> { const query = 'SELECT * FROM users WHERE id = $1'; const result = await db.query(query, [id]); return result.rows[0];}Recomendación: Utilice consultas parametrizadas u ORM (TypeORM, Prisma, Sequelize). Nunca concatenes cadenas para SQL.
3. Comparación insegura de contraseñas
Un error en la implementación de la comparación de contraseñas puede provocar ataques de temporización. Los desarrolladores de TypeScript suelen usar el operador === para comparar hashes, lo cual no es seguro.
Código vulnerable
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { const inputHash = await bcrypt.hash(inputPassword, 10); return inputHash === storedHash; // Vulnerabilidad: timing attack}Código corregido
import bcrypt from 'bcrypt';
async function verifyPassword( inputPassword: string, storedHash: string): Promise<boolean> { return bcrypt.compare(inputPassword, storedHash);}Recomendación: Utilice las funciones de comparación integradas de las bibliotecas de hashing (bcrypt.compare, argon2.verify). Están protegidas contra ataques de temporización.
4. Prototype Pollution mediante fusión recursiva de objetos
Las aplicaciones TypeScript suelen usar funciones de copia profunda o fusión de objetos. Si no se controlan las claves, un atacante puede contaminar el prototipo Object.prototype.
Código vulnerable
function merge(target: Record<string, any>, source: Record<string, any>) { for (const key in source) { if (typeof source[key] === 'object') { target[key] = merge(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target;}
// El atacante en