La seguridad no es una funcionalidad que se "añade al final": es una propiedad transversal que debe estar presente desde la primera línea del diseño. Esto es lo que significa seguridad por diseño (security by design). Un sistema que funciona, escala y está siempre disponible no sirve de nada si un atacante puede robar los datos de los usuarios o suplantar su identidad. En esta lección aprenderás los principios fundamentales de la seguridad (defensa en profundidad y mínimo privilegio), la diferencia esencial entre autenticación y autorización, los protocolos modernos que las implementan (OAuth2, OIDC y JWT), las vulnerabilidades más frecuentes según OWASP y, por último, cómo proteger los datos mediante cifrado en tránsito y en reposo.

Nota profesional: el contenido de esta lección es educativo sobre arquitectura. Cualquier implementación real de seguridad en un sistema de producción debe ser revisada por especialistas en seguridad y cumplir la normativa aplicable (por ejemplo GDPR/LOPDGDD).

Contenido

  1. Principios: defensa en profundidad y mínimo privilegio
  2. Autenticación vs Autorización
  3. OAuth2, OpenID Connect y JWT
  4. OWASP Top 10 resumido
  5. Cifrado en tránsito y en reposo
  6. Errores comunes y consejos
  7. Ejercicios

  1. Principios: defensa en profundidad y mínimo privilegio

Dos principios sostienen toda arquitectura segura.

La defensa en profundidad consiste en no confiar en una única barrera, sino en colocar múltiples capas de seguridad independientes. Si una falla, las demás siguen protegiendo. Es el principio del castillo medieval: foso, muralla, puertas, torres.

graph LR
    A[Internet] --> F[Firewall / WAF]
    F --> G[API Gateway + Auth]
    G --> S[Validación en el servicio]
    S --> D[(BD con permisos mínimos)]

Cada capa del diagrama protege de forma independiente: aunque un atacante atraviese el firewall, todavía debe superar la autenticación del gateway, la validación del servicio y los permisos de la base de datos.

El mínimo privilegio (least privilege) establece que cada usuario, servicio o proceso debe tener solo los permisos estrictamente necesarios para su función, ni uno más. Una cuenta de servicio que solo lee informes no debe poder borrar tablas.

-- MAL: la aplicación usa un usuario todopoderoso
GRANT ALL PRIVILEGES ON *.* TO 'app'@'%';

-- BIEN: solo los permisos que realmente necesita, sobre la base concreta
GRANT SELECT, INSERT, UPDATE ON tienda.* TO 'app'@'10.0.%';

En el ejemplo "MAL", si comprometen la aplicación, el atacante tiene control total de todas las bases de datos. En el "BIEN", solo puede leer y escribir en tienda, ni siquiera borrar (DELETE), y solo desde la red interna 10.0.%. El daño potencial queda contenido.

  1. Autenticación vs Autorización

Estos dos conceptos se confunden constantemente, pero son distintos:

Autenticación (AuthN) Autorización (AuthZ)
Pregunta ¿Quién eres? ¿Qué puedes hacer?
Verifica Identidad Permisos
Ocurre Primero Después (ya identificado)
Ejemplo Iniciar sesión con usuario y contraseña "¿Puede este usuario borrar facturas?"
Resultado típico Un token de identidad Permitir o denegar la operación

La regla mnemotécnica: AuthN = autenticNación (quién eres); AuthZ = autoriZación (qué puedes). Siempre se autentica primero y se autoriza después.

Los modelos de autorización más habituales son:

  • RBAC (Role-Based Access Control): los permisos se asignan a roles (admin, editor, lector) y los usuarios reciben roles. Simple y muy extendido.
  • ABAC (Attribute-Based Access Control): la decisión se basa en atributos (departamento, hora, ubicación, antigüedad). Más flexible y granular, pero más complejo.

  1. OAuth2, OpenID Connect y JWT

Estos tres términos suelen mezclarse. La clave para no confundirse:

Tecnología Para qué sirve Responde a
OAuth 2.0 Autorización delegada (dar acceso a recursos) ¿Qué puede hacer esta app en tu nombre?
OpenID Connect (OIDC) Autenticación (capa sobre OAuth2) ¿Quién es el usuario?
JWT Formato de token (transporta la info) (No es un protocolo, es un contenedor)

OAuth 2.0 permite que una aplicación acceda a recursos en nombre del usuario sin conocer su contraseña (por ejemplo, "Iniciar sesión con Google"). OIDC se construye encima de OAuth2 y añade la pieza que le faltaba: identificar al usuario, mediante un ID Token. JWT (JSON Web Token) es el formato más común en el que viajan esos tokens.

Un JWT tiene tres partes separadas por puntos: cabecera.payload.firma.

// Cabecera (header): algoritmo y tipo
{ "alg": "RS256", "typ": "JWT" }

// Payload (claims): la información, NO cifrada, solo codificada en Base64
{
  "sub": "usuario-123",        // subject: identificador del usuario
  "name": "Ana García",
  "roles": ["editor"],          // usado para autorización
  "iat": 1718000000,            // issued at: cuándo se emitió
  "exp": 1718003600             // expiration: cuándo caduca (1 hora después)
}

Punto crítico que confunde a los principiantes: el payload de un JWT no está cifrado, solo codificado en Base64. Cualquiera puede leerlo. Lo que garantiza la firma (la tercera parte) es la integridad y autenticidad: que nadie ha modificado el contenido y que lo emitió quien dice ser. Por eso nunca se deben poner secretos en un JWT.

// Validar un JWT firmado con RS256 (clave pública/privada)
Claims claims = Jwts.parserBuilder()
    .setSigningKey(clavePublica)   // Verificamos la firma con la clave pública del emisor
    .build()
    .parseClaimsJws(tokenRecibido) // Lanza excepción si la firma o la expiración no son válidas
    .getBody();

String userId = claims.getSubject();          // Extraemos el "sub"
List<String> roles = claims.get("roles", List.class);  // y los roles para autorizar

Este código verifica que el token no ha sido manipulado (gracias a la firma) ni ha caducado (exp), y solo entonces extrae el identificador y los roles. Si la validación falla, lanza una excepción y se rechaza la petición.

  1. OWASP Top 10 resumido

OWASP (Open Worldwide Application Security Project) publica periódicamente el Top 10 de los riesgos de seguridad web más críticos. Es lectura obligada. Resumen de las categorías principales:

# Categoría En qué consiste Mitigación clave
A01 Pérdida de control de acceso Acceder a recursos sin permiso Autorización en el servidor, denegar por defecto
A02 Fallos criptográficos Datos sensibles mal protegidos Cifrar en tránsito y reposo, no inventar criptografía
A03 Inyección (SQL, etc.) Datos maliciosos como código Consultas parametrizadas, validar entradas
A04 Diseño inseguro Fallos de diseño, no de código Modelado de amenazas desde el diseño
A05 Configuración incorrecta Valores por defecto, errores expuestos Endurecer (hardening), revisar configuración
A06 Componentes vulnerables Librerías obsoletas con CVE Actualizar, escanear dependencias
A07 Fallos de autenticación Sesiones débiles, fuerza bruta MFA, límites de intentos, gestión de sesión segura
A08 Fallos de integridad Datos/código sin verificar Firmas, verificar la cadena de suministro
A09 Fallos de registro/monitorización No detectar ataques Logs de seguridad, alertas, observabilidad
A10 SSRF Forzar al servidor a pedir URLs internas Validar y restringir destinos de las peticiones

El ejemplo clásico es la inyección SQL (A03):

// MAL: concatenar la entrada del usuario directamente en la consulta
String sql = "SELECT * FROM usuarios WHERE email = '" + email + "'";
// Si email = "' OR '1'='1", la consulta devuelve TODOS los usuarios.

// BIEN: consulta parametrizada; el motor trata "email" como dato, nunca como código
PreparedStatement ps = conn.prepareStatement(
    "SELECT * FROM usuarios WHERE email = ?");
ps.setString(1, email);   // El driver escapa el valor de forma segura

En la versión "MAL", un atacante puede inyectar SQL en el campo email y manipular la consulta. En la "BIEN", el ? es un marcador de posición: el motor nunca interpreta el valor del usuario como instrucciones SQL, eliminando la vulnerabilidad de raíz.

  1. Cifrado en tránsito y en reposo

Los datos deben protegerse en sus dos estados:

  • En tránsito (in transit): mientras viajan por la red. Se protege con TLS (el candado de HTTPS). Evita que alguien que intercepte el tráfico pueda leerlo (man in the middle).
  • En reposo (at rest): mientras están almacenados en disco, base de datos o copias de seguridad. Se protege cifrando el almacenamiento o columnas concretas.
En tránsito En reposo
Amenaza Interceptación en la red Robo del disco o backup
Tecnología TLS / HTTPS Cifrado de disco/columna (AES)
Ejemplo Conexión https:// Base de datos con cifrado transparente (TDE)
# Forzar HTTPS y redirigir todo el tráfico HTTP en Nginx
server {
    listen 80;
    return 301 https://$host$request_uri;   # Cualquier petición HTTP se redirige a HTTPS
}
server {
    listen 443 ssl;
    ssl_protocols TLSv1.2 TLSv1.3;          # Solo versiones modernas y seguras de TLS
    ssl_certificate     /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;
}

Este fragmento garantiza que nunca se sirva contenido por HTTP sin cifrar: el primer bloque redirige (código 301) todo el tráfico del puerto 80 a HTTPS. El segundo configura TLS permitiendo solo las versiones 1.2 y 1.3 (las antiguas tienen vulnerabilidades conocidas).

Una regla esencial sobre contraseñas: nunca se cifran ni se guardan en texto plano; se aplican funciones de hash lentas y con sal (bcrypt, scrypt, Argon2). Así, aunque roben la base de datos, no pueden recuperar las contraseñas originales.

Errores Comunes y Consejos

  • Confiar solo en el cliente. La validación y la autorización del navegador o la app móvil se pueden saltar trivialmente. Todo se valida y autoriza en el servidor.
  • Creer que el JWT está cifrado. No lo está. No metas datos sensibles en el payload; cualquiera puede leerlo.
  • Inventar tu propia criptografía. Casi siempre termina mal. Usa librerías y protocolos estándar, revisados y mantenidos.
  • Guardar contraseñas con MD5/SHA o en claro. Usa funciones de hash diseñadas para contraseñas (bcrypt, Argon2) con sal.
  • Mensajes de error demasiado detallados. Decir "el usuario no existe" frente a "contraseña incorrecta" ayuda al atacante. Responde de forma genérica.
  • No actualizar dependencias. Las librerías obsoletas (A06) son una de las vías de ataque más frecuentes. Escanea y actualiza.
  • Consejo: piensa como un atacante. En cada entrada de datos pregúntate "¿qué pasa si esto es malicioso?".

Ejercicios

  1. AuthN vs AuthZ. Un usuario inicia sesión correctamente pero, al intentar acceder al panel de administración, recibe un error 403 (Prohibido). ¿Falló la autenticación o la autorización? Justifícalo.

  2. Detectar la vulnerabilidad. Revisa este código y di qué riesgo del OWASP Top 10 contiene y cómo lo corregirías:

    String consulta = "SELECT * FROM productos WHERE nombre = '" + busqueda + "'";
    statement.executeQuery(consulta);
    
  3. Decisión de cifrado. Una aplicación de mensajería envía mensajes entre usuarios y los almacena en su base de datos. ¿Qué tipos de cifrado necesita y por qué cada uno?

Soluciones

  1. Falló la autorización (AuthZ), no la autenticación. El error 403 (Prohibido) significa que el sistema sabe quién es el usuario (se autenticó bien, no es un 401), pero ese usuario no tiene permisos para acceder al panel de administración. Si hubiera fallado la autenticación, el código sería 401 (No autorizado/no identificado).

  2. Es inyección SQL (A03): la variable busqueda se concatena directamente en la consulta, permitiendo a un atacante manipular el SQL. Corrección con consulta parametrizada:

    PreparedStatement ps = conn.prepareStatement(
        "SELECT * FROM productos WHERE nombre = ?");
    ps.setString(1, busqueda);
    ps.executeQuery();
    

    El ? impide que el valor del usuario se interprete como código SQL.

  3. Necesita ambos. Cifrado en tránsito (TLS/HTTPS) para que nadie pueda interceptar los mensajes mientras viajan por la red entre el dispositivo y el servidor. Y cifrado en reposo para proteger los mensajes almacenados en la base de datos frente al robo del disco o de una copia de seguridad. (Las apps más exigentes añaden cifrado de extremo a extremo, donde ni el propio servidor puede leerlos.)

Conclusión

Has aprendido que la seguridad se diseña desde el principio mediante capas (defensa en profundidad) y permisos mínimos, que autenticar (quién eres) es distinto de autorizar (qué puedes), que OAuth2/OIDC/JWT son las piezas estándar de la identidad moderna, que el OWASP Top 10 cataloga los riesgos que debes conocer, y que los datos se cifran tanto en tránsito como en reposo. La seguridad nunca es un producto terminado: es un proceso continuo de prevención, detección y mejora.

Y para detectar tanto ataques como problemas de rendimiento necesitamos ver qué ocurre dentro del sistema. En la siguiente lección, Observabilidad: Logging, Métricas y Trazabilidad, aprenderemos a instrumentar la aplicación para entender su comportamiento en producción.

Curso de Arquitectura de Aplicaciones

Módulo 1: Fundamentos de la Arquitectura de Aplicaciones

Módulo 2: Principios y Tácticas de Diseño

Módulo 3: Estilos y Patrones Arquitectónicos

Módulo 4: Arquitecturas Distribuidas y Microservicios

Módulo 5: Arquitecturas Dirigidas por Eventos y Mensajería

Módulo 6: Diseño Dirigido por el Dominio (DDD)

Módulo 7: Datos y Persistencia

Módulo 8: Arquitectura en la Nube y Despliegue

Módulo 9: Calidad, Seguridad y Observabilidad

Módulo 10: Evolución, Gobernanza y Casos Prácticos

© Copyright 2026. Todos los derechos reservados