FundamentosLección 11
Secretos y cadena de suministro
Dónde viven las contraseñas, claves y tokens de tu aplicación, qué hacer cuando se filtran, y cómo protegerte del código de terceros que compone el 90 % de tu software.
Al terminar sabrás
- Sacar los secretos del código y saber dónde guardarlos según el entorno.
- Reaccionar correctamente ante un secreto filtrado.
- Inventariar y vigilar dependencias con SBOM y escáneres.
- Endurecer el pipeline de CI/CD frente a ataques de cadena de suministro.
Puedes escribir un código impecable y aun así ser vulnerable por dos vías que no están en tu código: un secreto (una contraseña de base de datos, una clave de firma, un token de API) que acaba donde no debe, y una dependencia comprometida o vulnerable. Las dos categorías están en el OWASP Top 10:2025 (A02 configuración y A03 cadena de suministro).
Secretos: dónde NO deben estar
Sección titulada «Secretos: dónde NO deben estar»- En el código o en
application.ymlversionado. Borrarlo en el siguiente commit no sirve: sigue en el historial de git, en los forks y en las copias locales. - En el bundle del frontend. Cualquier variable que llegue al navegador es pública, aunque se llame
API_SECRET. - En imágenes de contenedor (
ENV DB_PASSWORD=…en el Dockerfile): quedan en las capas de la imagen. - En logs, mensajes de error o URLs (
?token=…acaba en logs de proxies e historiales).
Dónde SÍ
Sección titulada «Dónde SÍ»| Entorno | Opción | Comentario |
|---|---|---|
| Desarrollo local | Fichero .env fuera de git, perfiles locales | Valores de prueba, nunca los de producción |
| Contenedores / Kubernetes | Secretos montados como ficheros | Spring Boot los lee con configtree |
| Nube | Gestor de secretos (AWS Secrets Manager, OCI Vault, Azure Key Vault, HashiCorp Vault) | Auditoría, rotación, permisos finos |
| CI/CD y nube | Identidad federada (OIDC) en lugar de claves de larga duración | El pipeline obtiene credenciales temporales; no hay nada que filtrar |
En Spring Boot, el código solo conoce nombres, nunca valores:
spring: datasource: url: jdbc:postgresql://db:5432/notes username: ${DB_USER} password: ${DB_PASSWORD} # de una variable de entorno config: import: optional:configtree:/run/secrets/ # ficheros montados por Kubernetes/DockerCuando un secreto se filtra
Sección titulada «Cuando un secreto se filtra»- Rota primero: revoca el secreto y emite uno nuevo. Asume que ya se usó; los bots escanean GitHub en segundos.
- Revisa el uso: logs de acceso del proveedor, de la base de datos, de la nube.
- Limpia después: reescribir el historial de git es útil, pero no sustituye a la rotación.
- Previene: escaneo de secretos en el repositorio (GitHub push protection, gitleaks) y en CI.
La cadena de suministro
Sección titulada «La cadena de suministro»Tu aplicación Spring tiene decenas de dependencias directas y cientos de transitivas. Cada una es código que se ejecuta con tus permisos.
- Tu código
- Dependenciasdirectas y transitivasCVEtyposquattingdependency confusion
- Build / CIacciones, plugins, runnersacciones sin fijartokens con demasiados permisos
- Artefactojar, imagenimagen basefirma y procedencia
Lo que funciona:
- Inventario (SBOM): genera un Software Bill of Materials en cada build, por ejemplo con el plugin de CycloneDX para Maven o Gradle. Cuando salga el próximo Log4Shell sabrás en minutos si te afecta.
- Escaneo continuo: Dependabot o Renovate para actualizar; OWASP Dependency-Check, Snyk o Grype para detectar CVE. Mejor aún: usa el BOM de Spring Boot y actualiza la versión de parche con regularidad.
- Versiones fijadas: evita rangos (
[1.0,),latest). Un build reproducible es un build auditable. - Desconfía de los nombres: paquetes con nombres casi idénticos a los populares (typosquatting) y paquetes públicos con el mismo nombre que tus paquetes internos (dependency confusion).
- CI con mínimo privilegio: permisos mínimos del token del pipeline, acciones de terceros fijadas por SHA de commit (no por etiqueta), secretos solo en los jobs que los necesitan.
- Imágenes mínimas: imágenes base pequeñas (distroless, Alpine, imágenes de Paketo/Buildpacks), escaneadas, ejecutando como usuario no root.
Evita
permissions: write-alljobs: build: runs-on: ubuntu-latest steps: - uses: some-org/setup-tool@main - run: ./mvnw deploy env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY }}Prefiere
permissions: contents: read id-token: write # para OIDC con la nubejobs: build: runs-on: ubuntu-latest steps: - uses: some-org/setup-tool@3f1c2a9b8e7d6c5b4a39281706f5e4d3c2b1a098 # v4.2.0 - run: ./mvnw verifyMini lab · 15 min
Auditoría de un repositorio
Objetivo: Encontrar los problemas de secretos y de cadena de suministro en tres ficheros de un proyecto.
spring: datasource: password: Pr0d-2026!app: jwt-secret: c3VwZXItc2VjcmV0LWtleQ==FROM eclipse-temurin:latestENV STRIPE_KEY=sk_live_51H...COPY target/app.jar /app.jarENTRYPOINT ["java", "-jar", "/app.jar"]<dependency> <groupId>com.example</groupId> <artifactId>internal-utils</artifactId> <version>[1.0,)</version></dependency>Encuentra al menos cinco problemas y di cómo corregirías cada uno. Bonus: el secreto de producción lleva tres meses en el repositorio, ¿qué haces primero?
Pista
Revisa cada fichero con dos preguntas: ¿hay algún valor secreto escrito? ¿Hay algo que no esté fijado a una versión concreta?
Mostrar respuestaOcultar respuesta
- Contraseña de base de datos en el YAML versionado → variable de entorno o gestor de secretos.
- Secreto JWT en el YAML (y Base64 no es cifrado) → gestor de secretos; mejor aún, firmar con clave asimétrica guardada en un KMS.
- Clave de Stripe en el Dockerfile (
ENV) → queda en las capas de la imagen; inyectarla en tiempo de ejecución. - Imagen base
latest→ versión fija (y a ser posible por digest), una imagen mínima, escaneada, y ejecutar como usuario no root. - Rango de versiones
[1.0,)→ build no reproducible y riesgo de dependency confusion si un paquete público usa ese nombre. Versión fija y repositorio interno configurado para esosgroupId.
Bonus: rotar ya la contraseña de la base de datos, el secreto JWT (invalidará los tokens emitidos) y la clave de Stripe; después revisar los registros de uso de esos tres meses, y solo entonces limpiar el historial y activar el escaneo de secretos.