← Sesión, autenticación y Spring Security

UD9 · Proteger

Lo que debes recordar

El método

En esta unidad has aprendido a transformar una API abierta en un sistema seguro, gobernado por la identidad y los privilegios.

Para proteger cualquier aplicación web profesional, aplica siempre este decálogo de seguridad:

El decálogo de la arquitectura de seguridad
  1. Asume la amnesia congénita de HTTP: cada petición llega aislada y debe demostrar su identidad en cada llamada.
  2. NUNCA almacenes contraseñas en claro ni cifradas: utiliza siempre funciones hash criptográficas lentas con Salt (BCrypt con factor de coste 12).
  3. Separa conceptual y técnicamente la Autenticación (AuthN: ¿quién eres?) de la Autorización (AuthZ: ¿qué puedes hacer?).
  4. Respeta la semántica de códigos HTTP: responde 401 ante credenciales ausentes o inválidas y 403 ante permisos insuficientes.
  5. Diseña formalmente una Matriz de Control de Acceso (RBAC) antes de escribir una sola línea de código de seguridad.
  6. Aplica el principio de seguridad por defecto mediante una SecurityFilterChain funcional en Spring Security 6.
  7. Carga usuarios reales desde PostgreSQL desacoplando tu entidad JPA mediante el contrato UserDetailsService y UserDetails.
  8. Combina la seguridad perimetral de rutas con la seguridad en profundidad mediante anotaciones declarativas @PreAuthorize y expresiones SpEL.
  9. Elige con criterio técnico entre Sesión con Cookie (aplicaciones web tradicionales) o Token firmado JWT (APIs móviles y distribuidas sin estado).
  10. La seguridad nunca se fía del cliente: ocultar un botón en la interfaz es ergonomía; verificar el permiso en el backend es ingeniería.

La idea más importante

La seguridad nunca se implementa en el cliente; el navegador es un entorno que el usuario y el atacante controlan al 100 %. La única frontera real de una aplicación es el backend: toda regla de negocio, permiso y validación debe verificarse en el servidor en cada petición sin asumir jamás que el cliente es de confianza.

Ocultar botones o deshabilitar enlaces en una página web es una cortesía visual para que el usuario no se confunda. La verdadera seguridad consiste en que cuando un usuario intente saltarse esa interfaz y lanzar la petición a mano, el servidor detenga la operación en la frontera de red y responda con un muro infranqueable.

Las decisiones que tienes que saber justificar

Decisión de ingeniería Lo que tienes que poder defender ante un tribunal
BCrypt con factor 12 frente a SHA-256 SHA-256 es ultrarrápido y vulnerable a ataques de fuerza bruta masivos con GPUs; BCrypt es deliberadamente lento, incluye Salt aleatorio contra tablas arcoíris y su coste computacional es adaptable en el tiempo.
Diferenciación semántica entre 401 y 403 401 indica falta de autenticación válida (el cliente puede reintentar aportando credenciales); 403 indica que la identidad está verificada pero carece de privilegios suficientes (prohibido).
SecurityFilterChain funcional frente a herencia clásica WebSecurityConfigurerAdapter fue eliminado en Spring Boot 3; la configuración moderna mediante @Bean SecurityFilterChain con lambdas ofrece mayor modularidad y desacoplamiento.
UserDetailsService sobre entidad JPA Desacopla la lógica interna del modelo de datos de la aplicación del contrato técnico de seguridad requerido por el motor de autenticación de Spring.
@PreAuthorize con @EnableMethodSecurity Permite aplicar autorización granular junto al método de negocio, accediendo a los parámetros del método (#id) y evaluando reglas de propiedad a nivel de fila (SpEL).
API sin estado (STATELESS) con JWT Permite escalabilidad horizontal inmediata sin balanceo con sesiones pegajosas ni dependencias de clústeres de memoria compartida (Redis).
No guardar datos confidenciales en el Payload del JWT El Payload de un JWT solo está codificado en Base64Url y no cifrado; cualquier intermediario puede leer su contenido, por lo que solo debe contener identidades y roles.
Desactivación de CSRF solo en APIs con Bearer tokens Los tokens Bearer viajan en cabeceras añadidas manualmente por JavaScript y los navegadores nunca las envían de forma automática, haciendo el ataque CSRF técnicamente inviable.
Orígenes explícitos en CORS al usar credenciales El estándar W3C prohíbe el uso de allowedOrigins("*") junto a credenciales para evitar que cualquier sitio web malicioso secuestre sesiones autenticadas de usuarios.
Pruebas automáticas con @WithMockUser Garantiza que las restricciones de seguridad están blindadas por tests automatizados que se ejecutan en milisegundos en pipelines de integración continua sin depender de bases de datos.

Al terminar la unidad deberías poder responder

  1. ¿Por qué el protocolo HTTP es formalmente un protocolo sin estado (stateless)?
  2. ¿Cómo reconstruye el servidor la ilusión de continuidad mediante la cabecera Set-Cookie y el identificador JSESSIONID?
  3. ¿Qué peligros de seguridad neutralizan las directivas HttpOnly, Secure y SameSite en una cookie?
  4. ¿Cuál es la diferencia exacta entre autenticación (AuthN) y autorización (AuthZ)?
  5. ¿En qué escenario exacto una API REST debe responder con código 401 y en cuál con 403?
  6. ¿Qué es una Matriz de Control de Acceso (RBAC) y qué roles estructuran el gestor de proyectos?
  7. ¿Por qué las contraseñas nunca deben guardarse mediante cifrado simétrico reversible (como AES)?
  8. ¿Por qué un algoritmo de hash rápido como SHA-256 o MD5 se considera hoy una negligencia para contraseñas?
  9. ¿Qué función cumple el Salt aleatorio en un hash de contraseñas y qué ataque previene?
  10. ¿Qué representa el formato modular $2a$12$... en una cadena generada por BCrypt?
  11. ¿Qué ocurre con todos los endpoints de una API inmediatamente después de añadir la dependencia de Spring Security?
  12. ¿Cómo intercepta la SecurityFilterChain las peticiones HTTP antes de que alcancen a los controladores?
  13. ¿Qué contrato funcional exige la interfaz UserDetailsService y qué objeto devuelve?
  14. ¿Cómo permite la anotación @AuthenticationPrincipal inyectar al usuario activo en un controlador?
  15. ¿Qué diferencia práctica existe entre comprobar hasRole('ADMINISTRADOR') y hasAuthority('ROLE_ADMINISTRADOR')?
  16. ¿Qué ventaja ofrece la anotación @PreAuthorize frente a definir reglas de seguridad solo por rutas URL?
  17. ¿Qué tres partes componen la estructura de un JSON Web Token (JWT) y qué garantiza su firma?
  18. ¿Cuál es el compromiso (trade-off) más grave de usar JWTs puros sin estado respecto a la revocación de accesos?
  19. ¿Por qué un ataque de falsificación de peticiones en sitios cruzados (CSRF) solo afecta a sistemas basados en cookies?
  20. ¿Por qué el navegador bloquea una petición cross-origin si el backend tiene configurado allowedOrigins("*") y el cliente envía credenciales?

El vocabulario de la unidad

Concepto Significa
Stateless Característica de un protocolo o arquitectura donde el servidor no almacena ningún estado de sesión de los clientes entre peticiones consecutivas.
HttpSession Mecanismo de los contenedores de servlets de Java para mantener atributos y contexto de un usuario en la memoria del servidor.
HttpOnly Directiva de una cookie que prohíbe su acceso mediante código JavaScript (document.cookie), protegiéndola contra ataques XSS.
SameSite Atributo de cookie que restringe su envío en peticiones originadas desde sitios web cruzados para neutralizar ataques CSRF.
Autenticación (AuthN) Proceso de verificar la identidad declarada por un usuario o sistema mediante credenciales comprobables.
Autorización (AuthZ) Proceso de determinar si una identidad autenticada dispone de los privilegios necesarios para ejecutar una acción sobre un recurso.
401 Unauthorized Código de estado HTTP que indica que la petición carece de credenciales de autenticación válidas para el recurso solicitado.
403 Forbidden Código de estado HTTP que indica que el servidor ha verificado la identidad del cliente pero este carece de permisos suficientes.
RBAC Role-Based Access Control: modelo de seguridad donde los permisos se asignan a roles y los roles a usuarios.
BCrypt Función hash criptográfica adaptativa unidireccional basada en el cifrado Blowfish, deliberadamente lenta para resistir fuerza bruta.
Salt Secuencia aleatoria de bytes generada para cada usuario que se combina con su contraseña antes de computar el hash para evitar tablas precalculadas.
SecurityFilterChain Cadena ordenada de filtros de Spring Security que intercepta e inspecciona cada petición HTTP entrante antes del controlador.
UserDetailsService Interfaz central de Spring Security con el método loadUserByUsername() para recuperar credenciales y roles desde cualquier almacén de datos.
@PreAuthorize Anotación de Spring Security que evalúa expresiones de seguridad (SpEL) antes de permitir la ejecución de un método en servicios o controladores.
JWT JSON Web Token: estándar abierto (RFC 7519) que define un formato compacto y autónomo para transmitir información de forma segura mediante firmas criptográficas.
Bearer Token Esquema de autenticación HTTP donde el portador del token demuestra su autorización simplemente presentándolo en la cabecera Authorization.
CSRF Cross-Site Request Forgery: ataque que induce al navegador de una víctima autenticada a ejecutar acciones no deseadas en una aplicación web mediante cookies implícitas.

Comprobación final del producto de la unidad

Auditoría de seguridad backend · criterios de producción

  • Las contraseñas de los usuarios se almacenan exclusivamente con algoritmo BCrypt (factor de coste 12 o superior) y jamás en texto plano.
  • La identidad y los roles de los usuarios residen en tablas persistentes de PostgreSQL y se cargan mediante UserDetailsService.
  • Las cuentas de usuario desactivadas (activo = false) son rechazadas automáticamente por isEnabled().
  • La arquitectura de autorización distingue de forma estricta entre códigos 401 Unauthorized y 403 Forbidden.
  • Las rutas públicas de consulta y documentación (Swagger UI) están delimitadas sin exigir credenciales innecesarias.
  • Las operaciones destructivas (creación, edición y borrado) están protegidas por roles con @PreAuthorize o matchers de SecurityFilterChain.
  • La aplicación implementa una estrategia justificada de autenticación (sesión segura con cookies o tokens JWT sin estado).
  • Si se utiliza JWT, el token viaja en la cabecera Authorization: Bearer y la política de sesión es STATELESS.
  • La configuración de CORS está integrada formalmente en Spring Security y restringe los orígenes a clientes autorizados sin comodines universales (*).
  • La seguridad está respaldada por una suite de pruebas automatizadas con MockMvc y @WithMockUser que pasa al 100 % en verde.

Resultados de la unidad

  • Explicar por qué HTTP no mantiene estado y cómo lo resuelven cookies y sesión.
  • Distinguir autenticación de autorización.
  • Almacenar contraseñas con un algoritmo de hash adecuado.
  • Proteger endpoints por rol y por permiso con Spring Security.
  • Elegir entre sesión y token justificando la decisión.
  • Reconocer y corregir los fallos habituales de CSRF y de CORS con credenciales.

Ya deberías ser capaz de

  • Explicar por qué HTTP no mantiene estado y cómo lo resuelven cookies y sesión.
  • Distinguir autenticación de autorización.
  • Almacenar contraseñas con un algoritmo de hash adecuado.
  • Proteger endpoints por rol y por permiso con Spring Security.
  • Elegir entre sesión y token justificando la decisión.
  • Reconocer y corregir los fallos habituales de CSRF y de CORS con credenciales.