La seguridad no es una casilla que se marca y se olvida. Es una mentalidad, un proceso continuo y una responsabilidad que todo desarrollador tiene. Un solo error de seguridad puede costar millones en daños, destruir la confianza de los usuarios construida durante años e incluso acabar con las empresas. Pero la seguridad no tiene por qué ser intimidante o paralizante. Con el conocimiento adecuado y las prácticas establecidas, puede crear aplicaciones sólidas que protejan a sus usuarios.
El panorama de amenazas moderno
El mundo de la seguridad web ha cambiado drásticamente en los últimos años. Los atacantes ya no son piratas informáticos solitarios en sótanos oscuros: son organizaciones criminales sofisticadas con presupuestos millonarios, estados-nación con recursos ilimitados y robots automatizados que escanean Internet las 24 horas del día, los 7 días de la semana en busca de vulnerabilidades.
El coste de un ataque exitoso se disparó. No estamos hablando sólo de datos robados. Existen enormes multas regulatorias según GDPR y LGPD, que pueden alcanzar el 4% de los ingresos globales anuales de una empresa. Hay costos por notificar a los usuarios afectados, por la investigación forense, por la reparación de los sistemas y por el monitoreo del crédito de las víctimas. Y está el costo inconmensurable de la reputación destruida: usuarios que pierden la confianza y nunca regresan.
Pero el escenario también ha evolucionado en el lado de la defensa. Tenemos mejores herramientas, marcos más seguros por defecto y servicios administrados que eliminan clases enteras de vulnerabilidades. Los proveedores de la nube invierten miles de millones en seguridad de infraestructura. La comunidad de código abierto encuentra y corrige vulnerabilidades rápidamente. Si aprovecha estas herramientas y sigue las mejores prácticas, tiene posibilidades reales de adelantarse a los atacantes.
Las vulnerabilidades que realmente importan
OWASP publica una lista actualizada periódicamente de las 10 vulnerabilidades web más críticas. Esta lista no es académica: se basa en ataques reales y exitosos que cuestan dinero y datos a las empresas. Exploremos los más importantes y cómo defenderse.
Desglose del control de acceso
Esta es la vulnerabilidad número uno por una sencilla razón: es increíblemente común y devastadora. La idea básica es que los usuarios puedan acceder a recursos que no deberían. Bob puede ver las órdenes de Alice. Un usuario normal puede acceder a los puntos finales administrativos. Un cliente puede modificar los precios de los productos agregando un parámetro a la URL.
El error fundamental aquí es confiar en las aportaciones del usuario para tomar decisiones de seguridad. "Voy a ocultar este botón de administración en la interfaz de usuario" no es seguridad: cualquiera que conozca la URL puede acceder a ella. "Voy a poner ID secuenciales en la URL" es una invitación a recorrer los recursos.
La defensa comienza con comprobación de permisos en cada punto final. No sólo en la interfaz de usuario, sino también en el backend, en cada operación sensible. Cada solicitud debe responder a tres preguntas: ¿quién realiza esta solicitud? ¿Están autenticados? ¿Se les permite específicamente realizar esta acción en este recurso específico?
Utilice **identificadores no adivinables
vel** como UUID en lugar de ID secuenciales. Implemente políticas de privilegios mínimos: los usuarios solo deben tener los permisos mínimos necesarios para sus funciones. Y pruebe agresivamente: intente acceder a los recursos como otro usuario, como un usuario no autenticado, con ID modificadas.
Defectos criptográficos
Los datos sensibles se filtran constantemente porque no han sido protegidos adecuadamente. Esto incluye contraseñas almacenadas en texto claro o con hash débil, datos de tarjetas de crédito sin cifrar, tokens de sesión predecibles y copias de seguridad de bases de datos sin protección.
El principio fundamental es cifrar datos confidenciales en reposo y en tránsito. HTTPS (TLS) no es opcional para ningún sitio web en la Internet pública; los navegadores modernos incluso marcan los sitios web HTTP como "no seguros". Afortunadamente, los certificados TLS son gratuitos con Let's Encrypt.
Para datos en reposo, utilice cifrado seguro. AES-256 para datos simétricos. Nunca implemente su propia criptografía; utilice bibliotecas bien establecidas y auditadas. Para contraseñas específicamente, utilice algoritmos hash diseñados para contraseñas como bcrypt, scrypt o Argon2. Son intencionalmente lentos, lo que hace que los ataques de fuerza bruta no sean prácticos incluso con hardware moderno.
La administración de claves suele ser el eslabón débil. Las claves de cifrado no se pueden codificar en código o archivos de configuración en el repositorio. Utilice servicios de administración de secretos como AWS Secrets Manager, Google Secret Manager o HashiCorp Vault. Gire las llaves con regularidad. Contar con procedimientos para revocar claves comprometidas.
Inyección
La inyección SQL todavía prevalece porque es fácil de introducir accidentalmente y devastadora cuando se explota. Pero la categoría de inyección es más amplia: incluye inyección de comandos, inyección LDAP, inyección NoSQL y inyección de plantilla.
El patrón común es confiar en las entradas del usuario sin una desinfección adecuada, lo que permite a los atacantes inyectar comandos maliciosos. Un atacante puede extraer toda su base de datos, eliminar tablas, modificar datos o incluso hacerse con el control del servidor.
La defensa principal son declaraciones preparadas y consultas parametrizadas. En lugar de concatenar cadenas para crear consultas SQL, se utilizan marcadores de posición llenos de valores. La base de datos trata estos valores como datos, no como comandos, lo que hace imposible la inyección.
Los ORM modernos como Prisma, TypeORM o Sequelize hacen esto de forma predeterminada, por lo que el uso de estos marcos ya lo protege en la mayoría de los casos. Pero aun así hay que tener cuidado con las consultas sin formato cuando sea necesario.
La rigurosa validación de los datos de entrada es otra línea de defensa. Si espera un número, compruebe que en realidad sea un número. Si espera una fecha, valide el formato. Si espera una elección de una lista predefinida, verifique que el valor esté en esa lista. Nunca asuma que los datos de los clientes están seguros o están bien formados.
Secuencias de comandos entre sitios (XSS)
XSS permite a los atacantes inyectar JavaScript malicioso que se ejecuta en los navegadores de las víctimas. Esto puede robar cookies de sesión, modificar el contenido de la página, redirigir a sitios de phishing o instalar registradores de teclas.
Hay tres tipos principales: XSS almacenado (el script malicioso se guarda en la base de datos y se ejecuta cada vez que se carga la página), XSS reflejado (el script proviene de un parámetro de URL y se refleja en la respuesta) y XSS basado en DOM (la vulnerabilidad está en JavaScript del lado del cliente).
La defensa comienza con escapar de salidas. Cuando coloca datos de usuario en HTML, JavaScript, CSS o URL, debe utilizar caracteres especiales de escape de forma adecuada para ese contexto. Los marcos modernos como React hacen esto automáticamente en la mayoría de los casos, pero aún puedes introducir XSS usando dangerouslySetInnerHTML o similar.
La Política de seguridad de contenido (CSP) es una poderosa línea de defensa adicional. Es un encabezado HTTP que especifica qué fuentes de script, estilos, imágenes, etc. están permitidos. Incluso si un atacante logra inyectar código, CSP puede impedir su ejecución. Comience con una política restrictiva y ábrase según sea necesario.
Las cookies solo HTTP para tokens de sesión impiden que JavaScript acceda a estas cookies, lo que mitiga el impacto de XSS. Si un atacante no puede robar la cookie de sesión, el ataque es menos efectivo.
Exposición de datos confidenciales
Registros, mensajes de error, respuestas de API: todos estos son lugares donde se pueden filtrar accidentalmente datos confidenciales. Un seguimiento detallado de la pila en producción puede revelar la estructura y las dependencias del código. Los mensajes de error de SQL pueden exponer el esquema de base de datos. Los registros pueden contener contraseñas o tokens si no se tiene cuidado.
El principio es asumir que todo lo que envía al cliente puede ser visto por los atacantes. Esto significa no confiar nunca en la "seguridad a través de la oscuridad": ocultar información con la esperanza de que nadie la encuentre. Utilice verdadera autenticación y autorización.
Diferentes mensajes de error en producción frente a desarrollo es una buena práctica. En desarrollo, desea realizar seguimientos de pila detallados para la depuración. En producción, los usuarios (y los atacantes) deberían ver mensajes genéricos como "Se produjo un error inesperado".
Filtrar los registros cuidadosamente es esencial. Configure su registrador para que no registre campos confidenciales como contraseñas, tokens y números de tarjetas de crédito. Utilice enmascaramiento: registre solo los últimos 4 dígitos de una tarjeta, por ejemplo.
Autenticación y autorización sólidas
Estos son los guardianes de su sistema. La autenticación verifica la identidad (quién es usted), la autorización verifica los permisos (qué puede hacer). Los errores aquí son catastróficos.
Contraseñas y credenciales
Exigir contraseñas seguras es un buen comienzo, pero definir "fuerte" correctamente es importante. Las reglas arbitrarias como "debe tener un tipo para cada carácter" son menos efectivas que simplemente exigir una longitud mínima de 12 a 16 caracteres. Las frases de contraseña largas pero fáciles de recordar son mejores que las contraseñas cortas y complejas que los usuarios escriben en post-its.
Nunca, jamás almacene contraseñas en texto claro. Utilice algoritmos hash apropiados. Bcrypt con un factor de coste de al menos 10 es un buen estándar. El hashing debe ser lo suficientemente lento como para que la fuerza bruta sea poco práctica, pero no tan lento como para degradar la experiencia del usuario.
La limitación de velocidad en los puntos finales de inicio de sesión evita ataques de fuerza bruta. Después de algunos intentos fallidos, solicite CAPTCHA o bloquee temporalmente. Utilice un retroceso exponencial: cada intento fallido aumenta el tiempo de reutilización.
La autenticación multifactor (MFA) agrega una capa crítica de seguridad. Incluso si se filtra la contraseña, los atacantes no pueden acceder a la cuenta sin el segundo factor. TOTP (códigos de tiempo) a través de aplicaciones como Google Authenticator o Authy son buenos. Los SMS son mejores que nada, pero son vulnerables al intercambio de SIM. WebAuthn con claves de hardware (YubiKey) es el estándar de oro.
Gestión de sesiones
Los tokens de sesión son esencialmente claves para su aplicación. Si un atacante roba un token válido, puede hacerse pasar por el usuario.
Los tokens de sesión deben ser verdaderamente aleatorios e impredecibles. Utilice generadores criptográficamente seguros, no Math.random(). Los tokens deben tener entropía suficiente; se recomiendan al menos 128 bits.
Caducidad de la sesión equilibra la comodidad con la seguridad. Las sesiones muy largas suponen un riesgo si se filtra el token. Demasiado corto frustra a los usuarios. Considere la posibilidad de utilizar tokens de actualización: tokens de acceso de corta duración (entre 15 y 30 minutos) que se renuevan con tokens de actualización de larga duración pero que requieren una reautenticación periódica.
La invalidación de sesiones antiguas cuando el usuario cierra sesión o cambia la contraseña es crucial. Los atacantes no deberían poder utilizar tokens robados una vez que la víctima se dé cuenta del compromiso.
Conexión OAuth y OpenID
En la mayoría de los casos, no implemente la autenticación usted mismo. Utilice proveedores establecidos como Auth0, AWS Cognito, Firebase Auth o inicio de sesión social (Google, GitHub, Microsoft). Estos servicios especializados cuentan con equipos completos dedicados a la seguridad de la autenticación.
Si es absolutamente necesario implementarlo, utilice estándares establecidos. OAuth 2.0 para autorización, OpenID Connect para autenticación. No inventes tu propio sistema: el campo está lleno de trampas sutiles que son fáciles de pasar por alto.
PKCE (Clave de prueba para intercambio de códigos) debe usarse incluso en aplicaciones no públicas para evitar ataques de interceptación de códigos de autorización. Es una pequeña sobrecarga que elimina una clase de vulnerabilidades.
Defensa en profundidad
No confíes en una sola capa de protección. Suponga que cada capa puede fallar e implementar múltiples capas independientes.
Web Application Firewall (WAF) filtra el tráfico malicioso antes de que llegue a su aplicación. Servicios como Cloudflare, AWS WAF o Azure WAF bloquean firmas de ataques conocidos
. No sustituye al código seguro, pero es una capa adicional valiosa.
La limitación y limitación de velocidad evitan el abuso de las API. Limite la cantidad de solicitudes que un usuario puede realizar por minuto/hora. Esto mitiga los ataques DDoS, la fuerza bruta y el scraping agresivo.
Monitoreo y alertas detectan comportamientos sospechosos. ¿Muchos inicios de sesión fallidos desde una IP? ¿Actividad anormal de una cuenta normalmente inactiva? ¿Intentos de acceso a puntos finales administrativos por parte de usuarios habituales? Estos patrones deberían desencadenar alertas e investigaciones.
El plan de respuesta a incidentes garantiza que sepa qué hacer cuando (no si) ocurre un ataque. ¿A quién se le notifica? ¿Cómo aislar el sistema? ¿Cómo comunicarse con los usuarios? ¿Cómo recuperarse de las copias de seguridad? Tener un manual reduce drásticamente el tiempo de respuesta.
Conclusión
La seguridad web es un campo amplio y en constante evolución. Se descubren nuevas vulnerabilidades, surgen nuevos patrones de ataque y se crean nuevas herramientas de defensa. Nunca lo sabrás todo, pero puedes establecer principios y procesos sólidos que te mantengan por delante de la mayoría de las amenazas.
Comience con los fundamentos: [autenticación sólida, control de acceso adecuado, validación de entradas, salidas con escape, cifrado de datos confidenciales. Utilice marcos y bibliotecas bien establecidos en lugar de reinventar la rueda. Mantenga las dependencias actualizadas. Pruebe regularmente. Monitorear continuamente.
La seguridad no es un proyecto que se completa: es una práctica continua que se incorpora en todos los aspectos del desarrollo. Trátalo con la seriedad que se merece, porque tus usuarios te confían sus datos.
¿Cómo abordas la seguridad en tus proyectos? ¿Ha afrontado incidentes de seguridad? ¡Comparte tus experiencias!
Lea también
- Seguridad en Aplicaciones Web
- Seguridad en Aplicaciones Web - Arquitectura para Empresas
- Seguridad en aplicaciones web: la arquitectura explicada para principiantes
- Seguridad en aplicaciones web: los fundamentos que nadie puede ignorar
- Seguridad API
- Seguridad API: los pasos imprescindibles para no dejar la puerta abierta
