Mantener secrets (contraseñas de BD, claves de API, certificados) en application.yaml o variables de entorno es uno de los riesgos más comunes en producción. Una sola fuga del repositorio o de un dump de configuración expone toda la infraestructura.
Las soluciones modernas centralizan los secrets en un secret manager y los inyectan en la aplicación bajo demanda. Las dos opciones dominantes en el ecosistema Java son HashiCorp Vault (autoalojado o Vault Cloud) y Azure Key Vault (gestionado en la nube de Microsoft).
Anti-patrones que debes evitar
Antes de ver las soluciones, repasamos lo que no se debe hacer.
- 1. Commit de
application-prod.yaml: incluso en repos privados, cualquier desarrollador puede leerlos. Audita cada cambio. - 2. Variables de entorno con valores en claro: visibles en
ps,docker inspect, dashboards de Kubernetes. Son aceptables como referencia a un secret, no como contenedor del valor. - 3. Hardcoding en el código: aparece en backups, en historiales de Git aunque se borre, en logs de stack trace.
- 4. Secrets compartidos entre entornos: si filtras la clave de staging y es la misma que producción, ya tienes el incidente.
La regla de oro: en producción, ningún secret debe poder leerse desde un dump de la configuración o desde el código compilado. Solo desde el secret manager autenticado.
Arquitectura con HashiCorp Vault
flowchart LR
A[Spring Boot] -->|AppRole login| B[Vault]
B -->|Token con TTL| A
A -->|Read secret/data/db| B
B -->|JSON con username y password| A
A -->|Conexion JDBC| C[(PostgreSQL)]
D[Operador] -->|Rotar credencial| B
B -->|Notify| A
Los componentes son:
- Vault Server: cluster en HA con backend de almacenamiento (Consul, Raft).
- Engine
kv-v2: almacena secrets estáticos versionados. - Engine
database: genera credenciales dinámicas con TTL corto. - AppRole: método de autenticación pensado para servicios (RoleID + SecretID).
Spring Cloud Vault: configuración mínima
Añadimos la dependencia a Spring Cloud Vault.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
Y configuramos el application.yaml. Los datos de conexión a Vault vienen de variables de entorno, mientras que los secrets se proyectan como properties.
spring:
application:
name: pagos-service
cloud:
vault:
uri: ${VAULT_ADDR:https://vault.demo.local:8200}
authentication: APPROLE
app-role:
role-id: ${VAULT_ROLE_ID}
secret-id: ${VAULT_SECRET_ID}
role: pagos
kv:
enabled: true
backend: secret
default-context: pagos-service
application-name: pagos-service
Si en Vault hay un secret en secret/data/pagos-service con las claves db.username y db.password, Spring las expone como properties habituales.
spring:
datasource:
url: jdbc:postgresql://db.demo.local:5432/pagos
username: ${db.username}
password: ${db.password}
En Kubernetes, Vault Agent Injector monta los secrets como volúmenes y elimina la dependencia de
spring-cloud-vault. Es la opción preferida cuando el operador de Vault está en el mismo cluster.
Credenciales dinámicas de base de datos
El motor database de Vault genera usuarios efímeros de PostgreSQL/MySQL con TTL configurable. Cada microservicio recibe un usuario único, lo que mejora drásticamente la auditoría.
Configuración en Vault (CLI):
vault secrets enable database
vault write database/config/pagos \
plugin_name=postgresql-database-plugin \
allowed_roles="pagos-rw" \
connection_url="postgresql://{{username}}:{{password}}@db.demo.local:5432/pagos?sslmode=disable" \
username="vault" \
password="rootpwd"
vault write database/roles/pagos-rw \
db_name=pagos \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
Y en Spring:
spring:
cloud:
vault:
database:
enabled: true
role: pagos-rw
backend: database
Spring rota la credencial automáticamente y notifica a HikariCP cuando expira. La aplicación no ve el cambio.
Rotación de secrets sin downtime
La rotación es el verdadero motivo para usar un secret manager. Cuando un operador cambia la clave de un servicio externo, queremos que la aplicación la recoja sin reiniciarse.
Spring Cloud Vault expone el endpoint /actuator/refresh (necesita el starter spring-cloud-starter-bootstrap y spring-boot-starter-actuator). Una llamada POST reconstruye los beans anotados con @RefreshScope.
@Component
@RefreshScope
public class TwilioClient {
private final String apiKey;
public TwilioClient(@Value("${twilio.api-key}") String apiKey) {
this.apiKey = apiKey;
}
}
Tras rotar el secret en Vault, basta con curl -X POST http://app:8080/actuator/refresh para que el bean se reconstruya con la nueva clave.
Para flotas grandes, Spring Cloud Bus propaga el evento
RefreshEventpor RabbitMQ o Kafka, y todas las instancias se actualizan a la vez sin scripts manuales.
Azure Key Vault con Spring Cloud Azure
En Azure, la integración nativa es Azure Key Vault Secrets. Los secrets se referencian con la URL https://mi-vault.vault.azure.net/secrets/{nombre}.
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-keyvault-secrets</artifactId>
</dependency>
spring:
cloud:
azure:
keyvault:
secret:
property-sources:
- endpoint: "https://mi-vault.vault.azure.net"
Las identidades Managed Identity del recurso (App Service, AKS, VM) se autentican contra Key Vault sin guardar credenciales. La rotación se programa con Event Grid y la app la recoge en el siguiente ciclo de actualización.
Compara los SLA. Vault on-premise depende de tu equipo; Azure Key Vault tiene 99.99 % de disponibilidad pero también un coste por operación que puede sumar en sistemas con muchos lookups.
Cifrado de propiedades sensibles en disco
Cuando no es viable un secret manager (proyectos pequeños, ambientes air-gapped), una alternativa es cifrar las propiedades sensibles con Jasypt.
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
spring:
datasource:
password: ENC(K3yhE8W4j...)
La password de cifrado se inyecta por variable de entorno o por argumento de línea de comandos.
java -Djasypt.encryptor.password=secret -jar app.jar
Esto no es equivalente a Vault, pero protege frente a la lectura accidental del repositorio.
Auditoría de accesos al secret manager
Todo secret manager registra cada acceso. En una incidencia de seguridad, esos logs son la primera fuente de información para saber qué se ha leído y desde dónde.
- Vault: configurar
audit enable fileoaudit enable syslog. Los logs incluyen el path del secret, el cliente y el timestamp. - Azure Key Vault: activar Diagnostic Settings y enviarlos a Log Analytics.
- AWS Secrets Manager: integrarlo con CloudTrail.
Bloquea el acceso por políticas estrictas. El microservicio de pagos solo debería poder leer
secret/data/pagos/*, nosecret/data/usuarios/*. Una política bien definida limita el daño de un compromiso.
Errores frecuentes
- Cachear el secret indefinidamente: si la app guarda la API key en un campo
static final, la rotación no surte efecto. Usa@RefreshScopeo lee el secret en cada llamada (con cache de pocos segundos). - Loguear secrets: nunca loguees el resultado de
secret.getData(). Configura un filtro Logback que detecte y enmascare patrones tipopassword=.... - Compartir el AppRole entre microservicios: cada servicio debe tener su propio rol. Comprometer uno no debe dar acceso a los demás.
- Olvidar el plan de fallback: si Vault está caído, ¿la app arranca? Configura
spring.cloud.vault.fail-fast=truepara fallar rápido y delega en Kubernetes el reintento.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Security es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Spring Security
Explora más contenido relacionado con Spring Security y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Integrar Spring Boot con HashiCorp Vault y Azure Key Vault para leer secrets en arranque, refrescarlos en caliente con Spring Cloud Bus y rotar credenciales de base de datos.