Introducción: por qué la seguridad de las aplicaciones TypeScript es importante desde el primer día
TypeScript es una extensión de JavaScript que añade tipado estático. Muchos desarrolladores principiantes creen erróneamente que el tipado resuelve automáticamente todos los problemas de seguridad. En realidad, TypeScript se compila a JavaScript normal, y todas las vulnerabilidades inherentes a JS siguen siendo relevantes. En este artículo, analizaremos las 10 principales vulnerabilidades que enfrentan los principiantes y mostraremos cómo evitarlas.
1. XSS (Cross-Site Scripting)
XSS es uno de los ataques más comunes. Un atacante inyecta un script malicioso en la página que se ejecuta en el navegador del usuario.
Ejemplo de código vulnerable:
function renderUserInput(input: string): string { return `<div>${input}</div>`;}
// El atacante envía: <script>alert('XSS')</script>Cómo solucionarlo:
Escape siempre la entrada del usuario. Use bibliotecas como DOMPurify o métodos de escape integrados:
import DOMPurify from 'dompurify';
function safeRender(input: string): string { const clean = DOMPurify.sanitize(input); return `<div>${clean}</div>`;}2. Inyección SQL
Si construye consultas SQL concatenando cadenas con entrada del usuario, está abriendo la puerta a la inyección SQL.
Código vulnerable:
function getUser(username: string): Promise<User> { const query = `SELECT * FROM users WHERE username = '${username}'`; return db.execute(query);}Opción segura:
function getUserSafe(username: string): Promise<User> { const query = 'SELECT * FROM users WHERE username = $1'; return db.execute(query, [username]); // consulta parametrizada}3. Almacenamiento inseguro de contraseñas
Nunca almacene contraseñas en texto plano. Use bcrypt o argon2 para el hash.
import bcrypt from 'bcrypt';
async function hashPassword(password: string): Promise<string> { const saltRounds = 12; return bcrypt.hash(password, saltRounds);}
async function verifyPassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash);}4. Referencia directa insegura a objetos (IDOR)
Cuando un usuario puede acceder a datos de otros simplemente cambiando el ID en la URL.
Ejemplo:
// Vulnerable: cualquier usuario puede obtener cualquier pedidoapp.get('/api/order/:id', (req, res) => { const order = getOrderById(req.params.id); res.json(order);});
// Seguro: verificamos la pertenenciaapp.get('/api/order/:id', (req, res) => { const order = getOrderById(req.params.id); if (order.userId !== req.user.id) { return res.status(403).json({ error: 'Forbidden' }); } res.json(order);});5. Uso de dependencias obsoletas
Los proyectos TypeScript a menudo usan paquetes npm. Las versiones obsoletas pueden contener vulnerabilidades conocidas.
- Ejecute regularmente
npm audit - Use
npm updatepara actualizar paquetes - Configure verificaciones automáticas en CI/CD
6. Validación insuficiente de datos de entrada
Los tipos de TypeScript no se verifican en tiempo de ejecución. Siempre valide los datos en el servidor.
interface UserInput { email: string; age: number;}
// Insuficiente: los tipos de TypeScript no protegen en tiempo de ejecuciónfunction processUser(input: UserInput) { // ...}
// Correcto: use Zod o Joi para validaciónimport { z } from 'zod';
const UserSchema = z.object({ email: z.string().email(), age: z.number().min(18).max(120)});
function processUserSafe(input: unknown) { const validate