La seguridad en sistemas no es una capa que se agrega al final del desarrollo. Es una propiedad que debe estar presente desde las decisiones de arquitectura hasta cada línea de código que maneja datos sensibles. Para los desarrolladores, entender los fundamentos de seguridad no es opcional: es parte del oficio.
La mentalidad de seguridad
El primer cambio es de mentalidad: pasar de pensar "¿qué hace este sistema?" a también preguntarse "¿qué podría hacer alguien para abusar de este sistema?". Este modelo mental, conocido como threat modeling, ayuda a identificar superficies de ataque antes de que sean explotadas.
La práctica de Secure by Design propone que las decisiones de arquitectura incorporen seguridad desde el inicio: autenticación robusta, mínimo privilegio, validación de inputs en los bordes del sistema, cifrado de datos sensibles. No como parches posteriores, sino como características fundamentales del diseño.
Principio fundamental: Un atacante solo necesita encontrar un vector de ataque; el defensor necesita proteger todos. Esto hace que la seguridad sea asimétrica por naturaleza y justifica un enfoque proactivo, no reactivo.
Autenticación vs Autorización
La confusión entre autenticación y autorización es una de las fuentes más comunes de vulnerabilidades en sistemas. Son conceptos distintos con controles distintos:
- Autenticación: verifica quién eres. Proceso de confirmar la identidad de un usuario o sistema mediante credenciales (contraseña, biométrica, certificado, token).
- Autorización: verifica qué puedes hacer. Una vez autenticado, determina qué recursos puede acceder y qué operaciones puede ejecutar ese usuario.
Un sistema con autenticación fuerte pero autorización débil es vulnerable: un usuario autenticado puede acceder a recursos que no le pertenecen (horizontal privilege escalation) o escalar sus propios permisos (vertical privilege escalation). Ambos defectos aparecen frecuentemente en el OWASP Top 10.
JWT y tokens de acceso
JSON Web Tokens (JWT) son el mecanismo más utilizado para transmitir claims de autenticación en APIs REST. Un JWT consta de tres partes codificadas en Base64: header (algoritmo), payload (claims) y signature (firma que garantiza integridad).
Usa algoritmos asimétricos cuando sea posible
RS256 (RSA con SHA-256) o ES256 (ECDSA) son preferibles a HS256 (HMAC): con algoritmos asimétricos, los servicios que solo verifican tokens no necesitan conocer la clave privada con que se firmaron.
Tiempos de expiración cortos
Los access tokens deben tener vida corta (15-60 minutos). Los refresh tokens, vida larga pero almacenados de forma segura y con rotación. Un token comprometido con expiración corta limita drásticamente el daño.
Valida todos los claims
No solo verifiques la firma; valida iss (emisor), aud (audiencia) y exp (expiración). Aceptar tokens de cualquier emisor o para cualquier audiencia es una vulnerabilidad grave.
Cifrado en tránsito y en reposo
Los datos sensibles deben estar cifrados en dos estados distintos:
- En tránsito: toda comunicación HTTP debe ocurrir sobre TLS 1.2 o superior (HTTPS). Configura HSTS en el servidor para forzar HTTPS y prevenir downgrades.
- En reposo: bases de datos, archivos de configuración y backups que contienen datos sensibles deben estar cifrados. En Azure, usa cifrado transparente con claves gestionadas en Key Vault.
- Contraseñas: nunca almacenes contraseñas en texto plano ni reversiblemente. Usa funciones de hash diseñadas para contraseñas:
bcrypt,Argon2idoscrypt, con un factor de costo ajustado al hardware.
Nunca cifres contraseñas: cifrar una contraseña implica que puede descifrarse. Las contraseñas deben aplicárseles hash unidireccional. Si alguien con acceso a tu base de datos puede recuperar una contraseña, tu sistema tiene un defecto de diseño fundamental.
Principio de mínimo privilegio
El principio de mínimo privilegio establece que cada componente del sistema, ya sea un usuario, un proceso o un servicio, debe tener únicamente los permisos estrictamente necesarios para realizar su función. No más.
En la práctica:
- La cuenta de base de datos usada por la aplicación no necesita permisos de
DROP TABLEniCREATE; soloSELECT,INSERT,UPDATEyDELETEen las tablas requeridas. - Los service accounts en Kubernetes o cloud no necesitan acceso a todos los recursos del namespace o la suscripción.
- Los roles de usuario en la aplicación deben ser granulares: un usuario con rol de "lector" no debe poder escribir aunque encuentre un endpoint de escritura.
Manejo seguro de secretos
Uno de los problemas más comunes en proyectos es encontrar API keys, connection strings y contraseñas en el código fuente o en el historial de Git. Esto convierte el repositorio en una superficie de ataque permanente.
- Nunca hagas commit de secretos en el repositorio. Usa
.gitignorepara archivos.envlocales y audita el historial con herramientas comogit-secretsotruffleHog. - En desarrollo: usa
.envlocal ouser-secretsen .NET para mantener secretos fuera del código. - En producción: usa un servicio de gestión de secretos como Azure Key Vault, AWS Secrets Manager o HashiCorp Vault. Los secretos se inyectan en tiempo de ejecución, nunca en el código ni en las variables de entorno del pipeline de forma permanente.
Logging seguro
El logging es indispensable para detectar y responder a incidentes de seguridad. Pero un log mal diseñado puede convertirse en un vector de ataque o en una filtración de datos sensibles.
- Nunca logarithmizes datos sensibles directamente: contraseñas, tokens, números de tarjeta, datos personales. Si necesitas traza de una operación, loguea IDs anonimizados o hashes.
- Registra eventos de seguridad relevantes: intentos de autenticación fallidos, cambios de permisos, accesos a recursos privilegiados, errores de autorización.
- Protege los logs del acceso no autorizado: un atacante que puede modificar los logs puede borrar sus huellas.
Conclusión
La seguridad en sistemas es una disciplina extensa, pero sus fundamentos son accesibles para cualquier desarrollador. Autenticación robusta, autorización correctamente implementada, cifrado de datos sensibles, mínimo privilegio, gestión segura de secretos y logging apropiado constituyen la base que protege la mayoría de las superficies de ataque comunes.
Construir con seguridad desde el inicio no es significativamente más costoso que construir sin ella. Lo que sí es costoso —en tiempo, dinero y reputación— es remediar una brecha de seguridad después de que ocurre.