Seguridad de aplicaciones TypeScript: top 10 vulnerabilidades para principiantes
\n\nTypeScript es una extensión de JavaScript que añade tipado estático, pero no protege contra vulnerabilidades web clásicas. Los desarrolladores principiantes a menudo confían en el rigor de los tipos, olvidando la seguridad a nivel de lógica y datos. En este artículo analizaremos las 10 vulnerabilidades más comunes en aplicaciones TypeScript con ejemplos de código y métodos de protección.
\n\n1. XSS (Cross-Site Scripting) mediante dangerouslySetInnerHTML
\nUno de los problemas más frecuentes es la inserción de HTML de usuario sin sanitización. En TypeScript esto suele ocurrir al trabajar con React o Angular.
\n\n// ❌ Código vulnerable\nfunction UserComment({ comment }: { comment: string }) {\n return <div dangerouslySetInnerHTML={{ __html: comment }} />;\n}\n\n// ✅ Versión segura\nimport DOMPurify from 'dompurify';\n\nfunction SafeComment({ comment }: { comment: string }) {\n const sanitized = DOMPurify.sanitize(comment);\n return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;\n}\n\n2. Inyecciones SQL mediante consultas de cadena
\nTypeScript no protege contra inyecciones SQL si se utilizan cadenas sin procesar. Utilice siempre consultas parametrizadas.
\n\n// ❌ Peligroso\nconst query = `SELECT * FROM users WHERE email = '${userEmail}'`;\n\n// ✅ Seguro (ejemplo con Prisma)\nconst user = await prisma.user.findUnique({\n where: { email: userEmail }\n});\n\n3. Almacenamiento inseguro de secretos en el código
\nLas claves de API, tokens y contraseñas no deben estar en el código fuente. TypeScript se compila a JavaScript y todas las cadenas siguen siendo legibles.
\n\n// ❌ Nunca haga esto\nconst API_KEY = 'sk-1234567890abcdef';\n\n// ✅ Use variables de entorno\nconst API_KEY = process.env.API_KEY;\nif (!API_KEY) throw new Error('API_KEY is missing');\n\n4. Inyecciones NoSQL (MongoDB)
\nAl trabajar con MongoDB mediante Mongoose o el driver, se puede pasar accidentalmente un objeto de consulta que manipule la lógica.
\n\n// ❌ Código vulnerable\napp.post('/login', async (req, res) => {\n const user = await User.findOne({ \n email: req.body.email, \n password: req.body.password \n });\n});\n\n// ✅ Versión segura\napp.post('/login', async (req: Request, res: Response) => {\n const { email, password } = req.body;\n if (typeof email !== 'string' || typeof password !== 'string') {\n return res.status(400).send('Invalid input');\n }\n const user = await User.findOne({ email, password });\n});\n\n5. CSRF (Cross-Site Request Forgery)
\nSi su aplicación utiliza autenticación por cookies, un atacante puede realizar acciones en nombre del usuario.
\n\n// ❌ Sin protección\napp.post('/transfer', (req, res) => {\n // Realiza la transferencia sin verificar el origen\n});\n\n// ✅ Protección mediante token CSRF (ejemplo con csurf)\nimport csrf from 'csurf';\nconst csrfProtection = csrf({ cookie: true });\napp.post('/transfer', csrfProtection, (req, res) => {\n // Ejecución segura\n});\n\n6. Contaminación de prototipos (Prototype Pollution)
\nAl fusionar objetos sin verificación, se pueden sobrescribir propiedades del prototipo, lo que genera vulnerabilidades.
\n\n// ❌ Peligroso\nfunction merge(target: any, source: any) {\n for (const key in source) {\n target[key] = source[key];\n }\n}\n\n// ✅ Seguro\nfunction safeMerge(target: Record<string, any>, source: Record<string, any>) {\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n}\n\n7. Fuga de información mediante errores
\nLos stack traces y detalles de la base de datos no deben mostrarse al usuario. TypeScript puede dar una falsa sensación de seguridad debido al tipado estricto.
\n\n// ❌ Malo\napp.use((err, req, res, next) => {\n res.status(500).json({ error: err.m