Inyección SQL, XSS y CSRF: La Seguridad en el Frontend y Backend
Publicado el 26 de octubre de 2025
Inyección SQL, XSS y CSRF: La Seguridad en el Frontend y Backend
La seguridad web no es un “parche” que agregas al final. Es algo que debes considerar desde el primer día de desarrollo. Tres de los ataques más comunes y peligrosos en aplicaciones web son la inyección SQL, XSS (Cross-Site Scripting), y CSRF (Cross-Site Request Forgery).
Estos ataques pueden comprometer datos sensibles, robar sesiones de usuarios, y en casos extremos, tomar control completo de tu aplicación. Entender cómo funcionan es el primer paso para prevenirlos.
En este artículo, explicaré cómo funcionan realmente estos ataques, cómo se ven en código vulnerable, y cómo las herramientas modernas y las mejores prácticas los previenen. No incluiré código explícito de exploits, pero sí explicaré los mecanismos para que puedas proteger tus aplicaciones.
Parte I: Inyección SQL - La Autopsia
La inyección SQL es uno de los ataques más antiguos y peligrosos en aplicaciones web. Ocurre cuando un atacante puede inyectar código SQL malicioso en una consulta a la base de datos.
¿Cómo funciona la inyección SQL?
La inyección SQL funciona explotando la forma en que las aplicaciones construyen consultas SQL. Cuando una aplicación concatena directamente la entrada del usuario en una consulta SQL sin validación o sanitización, un atacante puede manipular esa consulta.
Imagina una aplicación que busca usuarios por nombre. Si el código construye la consulta concatenando directamente la entrada del usuario, un atacante podría ingresar algo como: admin' OR '1'='1 en el campo de búsqueda. Si la consulta se construye como SELECT * FROM users WHERE name = 'admin' OR '1'='1', la condición '1'='1' siempre es verdadera, lo que significa que la consulta devolvería todos los usuarios, no solo el que se buscaba.
Tipos de inyección SQL
Inyección SQL clásica: El atacante manipula la consulta para cambiar su lógica. Puede hacer que una consulta que debería devolver un resultado devuelva todos los resultados, o que una consulta que debería autenticar a un usuario autentique a cualquier usuario.
Inyección SQL basada en UNION: El atacante usa la cláusula UNION de SQL para combinar resultados de múltiples consultas. Esto permite extraer datos de otras tablas que no deberían ser accesibles.
Inyección SQL ciega: Cuando la aplicación no muestra errores SQL directamente, el atacante puede usar técnicas de “ensayo y error” para extraer información. Por ejemplo, puede verificar si una condición es verdadera o falsa basándose en el tiempo de respuesta o el comportamiento de la aplicación.
Inyección SQL basada en tiempo: El atacante usa funciones que causan retrasos (como SLEEP en MySQL) para determinar si una condición es verdadera. Si la aplicación tarda más en responder, sabe que la condición es verdadera.
¿Por qué es peligrosa?
La inyección SQL puede tener consecuencias devastadoras:
-
Acceso no autorizado: Un atacante puede autenticarse como cualquier usuario, incluyendo administradores.
-
Fuga de datos: Puede extraer información sensible de la base de datos: contraseñas, información personal, datos financieros.
-
Modificación de datos: Puede modificar o eliminar datos críticos.
-
Ejecución de código: En algunos casos, puede ejecutar comandos del sistema operativo en el servidor.
Cómo se ve en código vulnerable
En código vulnerable, verías algo como construir consultas SQL concatenando strings directamente con la entrada del usuario. Esto es extremadamente peligoso porque el atacante puede manipular la estructura de la consulta.
La vulnerabilidad típica ocurre cuando los desarrolladores no usan parámetros preparados o no validan la entrada del usuario antes de incluirla en consultas SQL.
Cómo lo previenen los ORMs modernos
Los ORMs (Object-Relational Mappers) modernos como TypeORM, Prisma, Sequelize, y otros previenen la inyección SQL de varias maneras:
Parámetros preparados (Prepared Statements): Los ORMs usan parámetros preparados automáticamente. En lugar de concatenar valores directamente en la consulta, los ORMs separan la estructura de la consulta de los valores. La base de datos compila la estructura de la consulta primero, y luego aplica los valores. Esto significa que incluso si un atacante intenta inyectar código SQL, la base de datos lo tratará como un valor literal, no como código ejecutable.
Validación de tipos: Los ORMs validan los tipos de datos antes de ejecutar consultas. Si esperas un número y recibes texto, el ORM rechazará la consulta antes de que llegue a la base de datos.
Escapado automático: Los ORMs escapan automáticamente caracteres especiales que podrían ser interpretados como código SQL.
Abstracción de consultas: Los ORMs proporcionan métodos de alto nivel para construir consultas. En lugar de escribir SQL crudo, usas métodos del ORM que construyen consultas seguras automáticamente.
Mejores prácticas para prevenir inyección SQL
-
Siempre usa ORMs o parámetros preparados: Nunca construyas consultas SQL concatenando strings con entrada del usuario.
-
Validación de entrada: Valida y sanitiza toda la entrada del usuario antes de usarla en consultas.
-
Principio de menor privilegio: Las conexiones a la base de datos deben usar usuarios con los mínimos privilegios necesarios. Si una aplicación solo necesita leer datos, no debería tener permisos de escritura.
-
Revisión de código: Revisa el código regularmente buscando patrones vulnerables. Cualquier lugar donde se construya SQL dinámicamente es un punto de atención.
-
Monitoreo y logging: Monitorea intentos de inyección SQL. Los patrones sospechosos en las consultas pueden indicar intentos de ataque.
Parte II: XSS y CSRF - Robando Sesiones en el Navegador
Mientras que la inyección SQL ataca el backend, XSS y CSRF atacan principalmente el frontend y la interacción entre el navegador y el servidor.
XSS (Cross-Site Scripting)
XSS es un ataque donde un atacante inyecta código JavaScript malicioso en una aplicación web que luego se ejecuta en el navegador de otros usuarios.
¿Cómo funciona XSS?
XSS funciona cuando una aplicación web incluye entrada del usuario en su HTML sin sanitizarla adecuadamente. Si un atacante puede hacer que la aplicación incluya código JavaScript malicioso, ese código se ejecutará en el navegador de cualquier usuario que visite la página.
Imagina un foro donde los usuarios pueden publicar comentarios. Si un atacante publica un comentario que incluye código JavaScript, y la aplicación muestra ese comentario sin sanitizar, el código se ejecutará en el navegador de todos los usuarios que vean ese comentario.
Tipos de XSS
XSS Reflejado (Reflected XSS): El código malicioso se refleja inmediatamente en la respuesta del servidor. Por ejemplo, si una aplicación muestra un mensaje de error que incluye la entrada del usuario sin sanitizar, un atacante podría crear un enlace que incluya código JavaScript. Cuando la víctima hace clic en el enlace, el código se ejecuta.
XSS Almacenado (Stored XSS): El código malicioso se almacena en la base de datos y se ejecuta cada vez que se muestra. Por ejemplo, si un atacante publica un comentario con código JavaScript, ese código se ejecutará cada vez que alguien vea ese comentario.
XSS Basado en DOM: El código malicioso se ejecuta completamente en el cliente, manipulando el DOM del navegador. No requiere que el servidor incluya el código malicioso en su respuesta.
¿Por qué es peligroso?
XSS puede tener consecuencias graves:
-
Robo de sesiones: El código JavaScript malicioso puede acceder a las cookies de sesión y enviarlas al atacante. Con las cookies de sesión, el atacante puede hacerse pasar por el usuario.
-
Manipulación de contenido: Puede modificar el contenido de la página para engañar a los usuarios, por ejemplo, cambiando formularios para que envíen datos al atacante.
-
Keylogging: Puede registrar las teclas que presiona el usuario, capturando contraseñas y otra información sensible.
-
Redirección maliciosa: Puede redirigir a los usuarios a sitios maliciosos.
Cómo se ve en código vulnerable
En código vulnerable, verías entrada del usuario siendo incluida directamente en HTML sin sanitización. Por ejemplo, si una aplicación toma el nombre de un usuario de la base de datos y lo muestra directamente en el HTML, un atacante podría registrarse con un nombre que incluya código JavaScript.
Cómo prevenir XSS
Sanitización de entrada: Toda la entrada del usuario debe ser sanitizada antes de mostrarse. Esto significa escapar caracteres especiales HTML como <, >, &, ", y '.
Content Security Policy (CSP): CSP es un mecanismo de seguridad que permite especificar qué fuentes de contenido son permitidas. Puedes usar CSP para prevenir la ejecución de JavaScript inline y restringir de dónde puede cargarse JavaScript.
HttpOnly cookies: Las cookies de sesión deben marcarse como HttpOnly. Esto previene que JavaScript acceda a ellas, incluso si hay un XSS.
Validación de entrada: Valida y sanitiza toda la entrada del usuario tanto en el frontend como en el backend.
Frameworks modernos: Frameworks modernos como React, Vue, y Angular escapan automáticamente el contenido, previniendo muchos tipos de XSS. Sin embargo, debes tener cuidado con dangerouslySetInnerHTML en React o v-html en Vue, que pueden ser peligrosos si no se usan correctamente.
CSRF (Cross-Site Request Forgery)
CSRF es un ataque donde un sitio web malicioso hace que el navegador de un usuario autenticado envíe una petición no deseada a otra aplicación web donde el usuario está autenticado.
¿Cómo funciona CSRF?
CSRF funciona explotando la confianza que una aplicación tiene en el navegador del usuario. Cuando un usuario está autenticado en una aplicación, el navegador envía automáticamente las cookies de sesión con cada petición a esa aplicación.
Imagina que un usuario está autenticado en su banco online. Si visita un sitio web malicioso mientras tiene la sesión del banco abierta, el sitio malicioso podría incluir código que hace que el navegador envíe una petición al banco para transferir dinero. Como el navegador envía automáticamente las cookies de sesión, el banco pensaría que es una petición legítima del usuario.
¿Por qué es peligroso?
CSRF puede tener consecuencias graves:
-
Acciones no autorizadas: Un atacante puede hacer que un usuario autenticado realice acciones sin su conocimiento: transferencias bancarias, cambios de contraseña, compras, etc.
-
Modificación de datos: Puede modificar datos del usuario sin su consentimiento.
-
Ataques en cadena: Puede combinarse con otros ataques para aumentar su efectividad.
Cómo se ve en código vulnerable
En código vulnerable, verías aplicaciones que aceptan peticiones de cambio de estado (POST, PUT, DELETE) sin verificar que la petición viene realmente del usuario y no de un sitio malicioso.
Cómo prevenir CSRF
CSRF Tokens: La forma más común de prevenir CSRF es usar tokens CSRF. La aplicación genera un token único para cada sesión y lo incluye en los formularios. Cuando el formulario se envía, la aplicación verifica que el token sea válido. Como los sitios maliciosos no pueden acceder a este token (debido a la política de mismo origen del navegador), no pueden crear peticiones válidas.
SameSite cookies: Los navegadores modernos soportan el atributo SameSite en las cookies. Cuando una cookie tiene SameSite=Strict, el navegador solo la envía en peticiones del mismo sitio. Esto previene que se envíe en peticiones desde sitios maliciosos.
Verificación de origen: Verifica que el header Origin o Referer de la petición coincida con tu dominio. Esto ayuda a detectar peticiones que vienen de otros sitios.
Double Submit Cookie: Similar a los tokens CSRF, pero el token se almacena tanto en una cookie como en el formulario. La aplicación verifica que ambos valores coincidan.
Re-autenticación para acciones críticas: Para acciones especialmente sensibles (como transferencias bancarias), requiere que el usuario se autentique nuevamente.
La importancia de la seguridad por capas
La seguridad efectiva no depende de una sola medida, sino de múltiples capas de protección:
-
Validación de entrada: Valida y sanitiza toda la entrada tanto en el frontend como en el backend.
-
Parámetros preparados: Usa ORMs o parámetros preparados para todas las consultas a la base de datos.
-
Sanitización de salida: Escapa o sanitiza toda la salida antes de mostrarla a los usuarios.
-
Cookies seguras: Usa cookies HttpOnly, Secure, y SameSite para proteger las sesiones.
-
HTTPS: Siempre usa HTTPS en producción para cifrar el tráfico entre el cliente y el servidor.
-
Headers de seguridad: Usa headers de seguridad como Content-Security-Policy, X-Frame-Options, y X-Content-Type-Options.
-
Principio de menor privilegio: Da a los usuarios y procesos solo los permisos mínimos necesarios.
-
Monitoreo y logging: Monitorea intentos de ataque y registra eventos de seguridad para análisis.
Mi perspectiva personal
La seguridad web no es algo que agregas al final del desarrollo. Es algo que debes considerar desde el primer día. He visto proyectos que funcionaban perfectamente pero que eran vulnerables a estos ataques básicos. Proyectos que perdieron datos, que fueron comprometidos, y que tuvieron que ser reescritos desde cero por problemas de seguridad.
Entender cómo funcionan estos ataques no es solo para expertos en seguridad. Es conocimiento fundamental que todo desarrollador debe tener. Porque al final del día, si no entiendes cómo funcionan estos ataques, no puedes prevenirlos efectivamente.
Los ORMs modernos y los frameworks de frontend han hecho mucho para prevenir estos ataques automáticamente. Pero no son una solución mágica. Aún necesitas entender los principios fundamentales, validar la entrada, sanitizar la salida, y seguir las mejores prácticas.
La seguridad es un proceso continuo, no un destino. Requiere vigilancia constante, actualización de dependencias, y revisión regular del código. Pero con el conocimiento correcto y las herramientas adecuadas, puedes construir aplicaciones que sean resistentes a estos ataques comunes.
Al final del día, la seguridad no es sobre hacer tu aplicación impenetrable (eso es imposible). Es sobre hacer que sea lo suficientemente difícil de atacar que los atacantes busquen objetivos más fáciles. Y entender cómo funcionan estos ataques es el primer paso para lograr eso.