Un secreto es un reloj
Una credencial estática no tiene estado interno. No sabe quién la presenta ni desde dónde. Lo único que la define en el tiempo es una ventana: el intervalo entre que se emite y que deja de ser aceptada. Dónde la guardás, si está cifrada en reposo, cuántas capas de KMS tiene arriba — todo eso son detalles de implementación sobre esa ventana.
La métrica que ordena el tema es el tiempo de exposición. Cuando un secreto se filtra, el intervalo en el que un atacante lo puede usar es:
T_exposición = T_detección + T_rotación, acotado por T_vida
T_detección es cuánto tarda alguien en enterarse de la filtración. T_rotación es cuánto tarda el equipo en emitir un reemplazo y sacar de circulación el viejo. T_vida es el techo: si el secreto expira solo, la ventana se cierra sin que nadie mueva un dedo.
Esa ventana se mide en años. GitGuardian contó 28,65 millones de secretos nuevos filtrados en repositorios públicos de GitHub durante 2025, un 34% más que el año anterior. El dato que importa acá es el otro: de las credenciales que su equipo confirmó válidas en 2022, el 70% seguía siendo válida en enero de 2025, y más del 64% seguía siéndolo al volver a testearlas en enero de 2026. La suma T_detección + T_rotación no es un número chico que se pueda dar por descontado.
Las tres decisiones grandes de la gestión de secretos atacan un término distinto de esa suma. Un gestor dedicado baja T_rotación. Un secret scanner baja T_detección. Las credenciales de corta vida bajan T_vida — y como T_vida acota a los otros dos, ahí está el apalancamiento.
Codecov sirve de calibración para el primer término. Según el post-mortem público de la empresa, un error en su proceso de creación de imágenes Docker permitió extraer una credencial, y con ella el atacante modificó el script Bash Uploader a partir del 31 de enero de 2021. Codecov se enteró el 1 de abril de 2021, cuando un cliente comparó el shasum del uploader publicado en GitHub contra el del archivo que se estaba bajando del servidor y encontró una diferencia. La divulgación pública salió el 15 de abril. Dos meses de T_detección, y la detección vino de afuera y por casualidad: no la produjo ningún control interno. Es el dato que conviene tener a mano cada vez que alguien propone un diseño que asume que una filtración se nota rápido.
El .env en el repo es un caso particular de un mecanismo de propagación
El .env commiteado se lleva toda la atención porque es visible. El mecanismo que lo hace peligroso es otro, y opera igual cuando el archivo no está en git.
Una variable de entorno es memoria del proceso, y fork() la copia. Todo hijo hereda el ambiente completo del padre, sin filtro. De ahí salen las rutas de fuga reales, que el cheat sheet de secrets management de OWASP enumera casi en el mismo orden:
- En Linux,
/proc/<pid>/environexpone el ambiente del proceso a cualquier lector que pase el chequeo de ptrace access mode: el mismo uid o root. En Kubernetes, los contenedores de un pod no comparten PID namespace por defecto, así que un sidecar no ve el/procdel contenedor principal salvo que el pod declareshareProcessNamespace: true. Las rutas que sí quedan abiertas son otras:kubectl execsobre el mismo contenedor, un agente de APM cargado dentro del proceso, un core dump, o cualquier proceso del host corriendo como root. - Los handlers de excepciones serializan contexto. Varios SDK de error tracking adjuntan el ambiente del proceso al evento, y ese evento sale del perímetro hacia un SaaS.
docker inspectdevuelve elEnvdel contenedor. Toda instrucciónENVdel Dockerfile queda escrita en la config de la imagen y viaja con eldocker pushal registry, disponible para cualquiera que pueda hacer pull. Los valores pasados con--build-argson un caso distinto: no se escriben en la config de la imagen, pero aparecen endocker historycuando el builder legacy los interpola dentro de unRUN, y quedan en cualquier archivo que eseRUNhaya escrito en una capa.- En Kubernetes, un
Secretes base64, no cifrado. SinEncryptionConfigurationen elkube-apiserver, el valor está en claro en etcd, y cualquiera conget secretsen el namespace lo lee. - Un
echode debug en un step del pipeline vuelca la variable al log. GitHub Actions y GitLab enmascaran los valores que registraron como secretos, pero el masking es matching de strings: si el valor se transforma —se parte, se codifica en base64, se mete adentro de un JSON— el filtro no lo reconoce. - Cualquier proceso hijo lanzado desde la aplicación —un hook, un
sh -c, una herramienta de línea de comandos— recibe el ambiente entero, aunque solo necesite una variable.
Hay una ruta más que vive fuera del proceso y conviene nombrar acá: el state de Terraform. HashiCorp lo documenta de forma explícita en la página "Sensitive Data in State" — el state guarda en texto plano cualquier atributo que el provider devuelva, incluidos los sensibles, y por eso recomienda tratar el archivo de state con el mismo cuidado que a los secretos que contiene, con backend remoto, cifrado en reposo y control de acceso.
La consecuencia práctica: el ambiente de un proceso funciona como canal de difusión. Todo lo que se escribe ahí se copia a cada hijo y se serializa en cada volcado de contexto. Sacar el .env del repo cierra una ruta de las seis. Es la más visible y la más barata de cerrar, y por eso se confunde con el problema.
Qué hace un gestor dedicado por dentro
Un gestor de secretos —Vault, AWS Secrets Manager, GCP Secret Manager— cambia el mecanismo de entrega. En lugar de escribir el valor en el ambiente del proceso, la aplicación se autentica y lo pide en runtime. Tres cosas se caen de ese cambio.
La primera es la auditoría. Cada lectura es una llamada API con identidad, timestamp e IP. Un archivo .env no deja registro de lecturas, así que una filtración por esa vía solo se detecta por un canal externo — el uso anómalo, la factura, un tercero que avisa. Codecov es exactamente ese patrón.
La segunda es la revocación. Si la aplicación lee el secreto en cada arranque, cambiar el valor en el gestor y reiniciar alcanza.
La tercera es la que importa: el gestor puede emitir el secreto en vez de guardarlo. El database secrets engine de Vault se configura con una credencial de administrador y, ante cada pedido, ejecuta un CREATE ROLE contra el motor y devuelve un usuario nuevo con un lease asociado.
# el rol api-readonly está configurado con default_ttl=1h y max_ttl=24h
vault read database/creds/api-readonly
# lease_id database/creds/api-readonly/8xK2mQ...
# lease_duration 1h
# username v-token-api-read-3f2a91c4
# password A1a-...
Ese default_ttl hay que ponerlo. Si el rol no lo declara, el TTL cae al default del mount y del sistema, que en un Vault de fábrica son 768 horas — treinta y dos días de credencial "dinámica", que es casi lo mismo que una estática con más pasos.
Cuando el lease vence, Vault ejecuta el statement de revocación y el usuario deja de existir. T_vida pasa a ser un parámetro de configuración en vez de una propiedad emergente de qué tan olvidadizo es el equipo.
El costo de esto es el secreto cero: la aplicación necesita una credencial para autenticarse contra el gestor. Si esa credencial es un token estático en un .env, movimos el problema una capa y ganamos auditoría. La forma de cerrarlo es que la identidad venga de la plataforma: un ServiceAccount token proyectado de Kubernetes contra el auth method de Kubernetes de Vault, un rol de instancia de AWS, un JWT del CI. Ninguno de esos es un string que alguien pueda copiar y pegar en Slack.
Ahora, el umbral. Las variables de entorno del CI alcanzan mientras se cumplan tres condiciones a la vez: pocos repositorios, un solo entorno productivo, y proveedores que no ofrecen federación. Es una configuración legítima y no hay que apurarse a romperla.
Dejan de alcanzar el día que no podés responder "qué repositorios tienen esta credencial" sin abrir la UI de a un repo por vez. Esa pregunta es la que define el T_rotación de una emergencia, porque rotar significa actualizar a todos los consumidores, y no podés actualizar lo que no podés listar. Si la respuesta requiere inspección manual, el inventario ya se perdió.
Y meter un gestor dedicado rompe algo concreto: la aplicación pasa a tener una dependencia dura de disponibilidad en el arranque del pod. Si el gestor está caído durante un evento de escalado o un rollout, los pods nuevos no levantan. Se mitiga con Vault Agent inyectando el valor en un archivo del pod y cacheando localmente, o con el External Secrets Operator materializando un Secret nativo que sobrevive a la caída del gestor. Las dos mitigaciones reintroducen una copia del valor en el cluster. Es un intercambio válido, siempre que se tome como decisión consciente y quede escrito.
OIDC: emitir en vez de guardar
El caso más limpio de T_vida cortado en la raíz es la federación OIDC entre el CI y el cloud. El mecanismo tiene cuatro pasos.
El runner pide un JWT al servicio de identidad de la plataforma de CI. En GitHub Actions eso requiere permissions: id-token: write en el job, que habilita las variables ACTIONS_ID_TOKEN_REQUEST_URL y ACTIONS_ID_TOKEN_REQUEST_TOKEN. El token lo firma GitHub y lleva claims que describen el contexto de ejecución: iss apunta a token.actions.githubusercontent.com, sub codifica el repo y el disparador —por ejemplo repo:acme/api:environment:production—, más repository, ref, sha, workflow y job_workflow_ref.
El cloud valida esa firma contra el JWKS público del emisor, publicado en /.well-known/openid-configuration. La confianza es criptográfica y la clave pública es pública: el JWT viaja legible y no hay ningún valor compartido que custodiar.
El cloud evalúa una trust policy contra los claims. En AWS eso es un rol de IAM con sts:AssumeRoleWithWebIdentity y condiciones sobre aud y sub.
data "aws_iam_policy_document" "ci_deploy" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github.arn]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:acme/api:environment:production"]
}
}
}
Y el cloud devuelve credenciales temporales de sesión. La documentación de IAM lo plantea como una propiedad de diseño: las credenciales temporales expiran solas, no se almacenan junto al usuario, y no hay que rotarlas ni revocarlas porque dejan de servir. El rango va de 15 minutos a 12 horas según el caso.
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111122223333:role/ci-deploy
aws-region: us-east-1
role-duration-seconds: 900
aws-actions/configure-aws-credentials pide una hora por defecto. Bajalo. El mínimo de AssumeRoleWithWebIdentity es 900 segundos, y un terraform apply o un aws s3 sync terminan mucho antes. Un deploy no necesita una hora de crédito abierto, y cada minuto de más es T_vida regalado.
GitLab tiene el mismo mecanismo con otra sintaxis. El job declara id_tokens con la audiencia que espera el provider, y GitLab inyecta el JWT en la variable indicada.
deploy:
id_tokens:
AWS_ID_TOKEN:
aud: https://gitlab.com
script:
- >
creds=$(aws sts assume-role-with-web-identity
--role-arn "$AWS_ROLE_ARN"
--role-session-name "gitlab-$CI_PIPELINE_ID"
--web-identity-token "$AWS_ID_TOKEN"
--duration-seconds 900
--query 'Credentials' --output json)
Del lado de AWS cambian el iss (https://gitlab.com) y el formato del sub, que en GitLab tiene la forma project_path:grupo/proyecto:ref_type:branch:ref:main. El resto del razonamiento es idéntico.
No hay aws-access-key-id en ningún lado. No hay nada que rotar, nada que filtrar en un log que sirva mañana, nada que un ex empleado se pueda llevar. T_vida son quince minutos y el término domina la suma entera.
El mecanismo también te dice exactamente dónde falla. La seguridad vive en el StringEquals sobre sub. Si esa condición se escribe con StringLike y un comodín ancho —repo:acme/*:*— cualquier workflow de cualquier repo de la organización asume el rol de producción, incluido uno que alguien agregue mañana. Peor: un comodín que no ancle el prefijo de la organización deja que un repo ajeno se haga pasar por el tuyo. El aud conviene fijarlo también, aunque ahí el riesgo es menor de lo que suele contarse: el OIDC provider de IAM ya valida la audiencia contra la lista de client IDs registrados en el provider, así que la condición explícita funciona como defensa en profundidad y como documentación de la intención. El blast radius de la federación es exactamente igual de ancho que el claim contra el que hacés match.
Rotar a mano no escala, y el motivo es de diseño
La rotación tiene un problema de concurrencia que no depende del tamaño del equipo. Entre que emitís la credencial nueva y que el último consumidor la adopta, hay un intervalo donde conviven dos valores. Si invalidás el viejo al principio de ese intervalo, se corta el servicio. Si lo invalidás al final, hay que saber cuándo terminó.
AWS Secrets Manager resuelve eso con staging labels sobre las versiones del secreto. La rotación es una función Lambda con cuatro pasos que el servicio invoca en orden: createSecret genera el valor nuevo y lo marca AWSPENDING; setSecret lo aplica en el sistema de destino; testSecret verifica que funcione; finishSecret mueve la etiqueta AWSCURRENT a la versión nueva y degrada la anterior a AWSPREVIOUS. Los clientes que piden el secreto sin especificar versión reciben siempre AWSCURRENT, así que el switch es atómico desde el punto de vista del consumidor.
El detalle que hace que esto funcione en bases de datos es la estrategia de dos usuarios: la rotación alterna entre dos cuentas y rota siempre la que no está en uso. El usuario activo nunca cambia de contraseña mientras hay conexiones abiertas contra él.
La máquina de estados resuelve el lado del gestor. Del lado de la aplicación queda un bloqueante que ninguna plataforma puede cerrar por vos: una aplicación que lee el secreto al arrancar y se lo entrega a un connection pool no vuelve a consultar el gestor nunca más. El pool sostiene las conexiones que ya autenticó, así que sigue funcionando con la credencial vieja hasta que necesite abrir una conexión nueva — o hasta el próximo restart. El síntoma aparece horas o días después de la rotación, desacoplado del cambio, cuando el pool recicla una conexión y la contraseña vieja ya no existe. Releer el secreto y reconstruir el pool es trabajo de aplicación. El External Secrets Operator sincroniza el valor hacia un Secret del cluster con un refresh interval, y Vault Agent lo reescribe en un archivo y puede disparar un comando, pero ninguno de los dos fuerza a tu driver a reautenticar. Ahí está el otro motivo por el que la estrategia de dos usuarios vale: da un ciclo entero de rotación de margen para que los consumidores se pongan al día.
Rotar y revocar son dos operaciones distintas. Rotar es emitir una credencial nueva y poner a los consumidores a usarla. Revocar es hacer que la vieja deje de ser aceptada. Una rotación sin paso de revocación te deja dos credenciales válidas, y la que se filtró es una de ellas. El runbook necesita el paso de revocación escrito con dueño y comando exacto: aws iam delete-access-key, vault lease revoke, DROP ROLE, revocar la app desde el panel del proveedor.
El post-mortem de Cloudflare del incidente de Thanksgiving 2023 es la mejor ilustración pública de ese hueco. Después del breach de Okta de octubre de 2023, el equipo rotó miles de credenciales, pero dejó cuatro sin rotar —un service token de Moveworks y tres cuentas de servicio— porque las creyó sin uso. Con esas cuatro el atacante volvió a entrar un mes después. La detección funcionó rápido: la alerta automática saltó a los dos minutos del cambio de permisos, el 23 de noviembre a las 16:00, y la cuenta quedó desactivada a las 16:35. La respuesta terminó siendo rotar todas las credenciales de producción, más de cinco mil. El costo no estuvo en el mecanismo de rotación sino en el inventario de qué credenciales existían y quién las usaba.
Dos clases de secreto hay que diseñarlas rotables el día uno, porque después rotar deja de ser una tarea y pasa a ser un proyecto. La clave de firma de JWT: si el verificador acepta una sola clave, rotarla invalida todos los tokens en vuelo. Con varias claves activas publicadas en el JWKS y un kid en el header de cada token, el verificador elige por kid y la rotación se vuelve un solapamiento controlado. La clave de cifrado en reposo: rotarla obliga a recifrar todo lo cifrado con la anterior. El diseño que lo hace barato es envelope encryption — una data key por objeto, cifrada con la key maestra, de modo que rotar la maestra sea recifrar las data keys y no los datos.
Reproducir la máquina de estados a mano, bajo presión, a las tres de la mañana, con una lista incompleta de consumidores, es donde la rotación manual se rompe. Y se rompe justo cuando más la necesitás: durante un incidente. Un equipo que nunca rotó en frío no va a rotar bien en caliente. Por eso la rotación programada vale aunque no haya ninguna sospecha de filtración — es el ensayo del procedimiento de emergencia.
Qué ve un secret scanner que un code review no ve
El code review tiene tres límites estructurales, y ninguno se arregla con más disciplina.
Alcanza solo al diff. Un secreto que entró hace dos años no aparece en ninguna revisión de hoy, y en git la historia es inmutable: git rm del archivo deja el blob intacto. El paper de Meli, McNiece y Reaves (NDSS 2019) midió eso a escala sobre miles de millones de archivos de GitHub público: miles de secretos únicos filtrados por día, más de cien mil repositorios afectados, y una porción sustancial de los secretos "borrados" siguiendo accesible en el historial.
No verifica. Un revisor ve un string de 40 caracteres y no sabe si es una key activa, una de un entorno muerto o un placeholder. La duda empuja a dejarlo pasar.
Y ocurre una vez. Después del merge, nadie vuelve a mirar.
Un scanner ataca los tres. Recorre todos los commits alcanzables:
# búsqueda manual sobre toda la historia
git log --all -S 'AKIA' --oneline
# gitleaks v8: el subcomando 'detect' quedó deprecado a favor del split git/dir
gitleaks git . --redact
gitleaks dir . --redact
# pre-commit: reemplaza a 'gitleaks protect --staged'
gitleaks git --staged --redact
# trufflehog: verificación activa contra el proveedor
trufflehog git file://. --only-verified
Los subcomandos de gitleaks se reorganizaron en la línea v8; chequeá gitleaks --help contra la versión que vas a fijar en el pre-commit antes de copiar cualquier flag.
Ese --only-verified de trufflehog es el punto interesante del mecanismo: en vez de reportar por forma, hace una llamada de bajo costo contra el proveedor para confirmar que la credencial responde. Separa los hallazgos de entropía alta de los que un atacante puede usar ahora mismo, y esa priorización es lo que sostiene la adopción de la herramienta a lo largo del tiempo.
El secret scanning de GitHub agrega dos piezas que una herramienta local no puede tener. La primera es el partner program: según la documentación de GitHub, los proveedores de servicios registran los patrones de sus propias credenciales, y cuando el escaneo detecta un match en un repositorio público GitHub le notifica al proveedor, que puede revocar o reemitir la credencial del lado del emisor. Ahí T_detección y T_rotación colapsan a minutos sin que el dueño del repo intervenga — y solo aplica a repos públicos y a los proveedores que participan. La segunda es push protection, que corre el detector en el push y lo rechaza si encuentra algo. Mueve la detección de después del commit a antes, y es la única variante donde T_exposición puede ser cero.
Cerrar el ciclo requiere entender una propiedad de git y de GitHub que sorprende a mucha gente. Reescribir la historia con git filter-repo o BFG no basta: los forks conservan los objetos, las refs de pull request quedan, y un commit desreferenciado sigue siendo accesible por su SHA hasta que el garbage collector del servidor pase — si pasa. La documentación de GitHub sobre cómo remover datos sensibles de un repositorio lo dice sin rodeos: una vez que el commit se pusheó, hay que considerar comprometido todo secreto que contenga y rotarlo, además de limpiar la historia. Rotar es la acción que cierra la ventana. La limpieza de la historia es higiene posterior.
La comparación, ordenada por tiempo
| Mecanismo | T_vida | Cómo se revoca | Costo de rotación |
|---|---|---|---|
.env commiteado | indefinido | rewrite de historia más rotación manual | alto y no acotado |
| Variable de entorno en el CI | hasta que alguien la edita | a mano, en la UI, una por una | manual, propenso a omisiones |
| Gestor dedicado, valor estático sin política | indefinido | update central más restart del consumidor | automatizable |
| Gestor dedicado, valor estático con rotación programada | el intervalo de rotación | la Lambda de rotación, sola | bajo, una vez escrita |
| Gestor dedicado, credencial dinámica | TTL del lease | expira sola o vault lease revoke | cero |
| OIDC con rol asumido | duración de la sesión (mínimo 900 s) | expira sola | no aplica |
Leída por la columna de la derecha, la tabla es una escalera de migración. El orden que más recorta la ventana por unidad de trabajo: push protection, que frena secretos nuevos; OIDC en el CI, que elimina la clase entera de credenciales estáticas de deploy; credenciales dinámicas para las bases de datos; y al final los secretos de terceros que no soportan nada mejor que un valor estático rotado por calendario.
Las herramientas, y qué hace cada una
| Herramienta | Qué término de la suma ataca | Nota |
|---|---|---|
| Gitleaks | T_detección | Scanner de git con reglas en TOML y baseline. Escanea la historia completa, no el HEAD. Gratis. |
| TruffleHog | T_detección | Verificación activa contra el proveedor sobre más de 800 tipos de credencial. Core open source. |
| GitHub Push Protection | T_exposición a cero | Bloquea el push antes de que el secreto entre a la historia. Gratis en repos públicos; en privados requiere GitHub Secret Protection. |
| SOPS | T_rotación | Cifra los valores del YAML/JSON dejando las claves en claro, con KMS, age o PGP. El escalón intermedio realista antes de montar un gestor. Proyecto CNCF. |
| HashiCorp Vault | T_vida y T_rotación | Los secrets engines dinámicos son lo relevante acá: lease, TTL y revocación automática. Community Edition gratis bajo BUSL. |
| External Secrets Operator | T_rotación | Sincroniza desde el gestor hacia Secrets del cluster con refresh interval, así rotar se propaga sin redeploy. Apache-2.0. |
Qué medir
El programa de secretos se evalúa con tres números, y los tres son tiempos.
La edad del secreto más viejo en producción: el peor caso de T_vida, y el número que un auditor pide primero.
El tiempo desde el commit hasta la alerta, medido con un canario: un secreto real pero sin permisos, plantado a propósito, con una alarma en su uso. Es la forma honesta de medir T_detección, porque ejercita el sistema completo —scanner, notificación, alguien mirando— y no el detector aislado.
El tiempo de rotación de una credencial de producción, cronometrado en un ejercicio en frío, de punta a punta, incluyendo la verificación de que ningún consumidor quedó atrás.
El runbook, en cinco pasos
Escribilo antes de necesitarlo y ensayalo en frío. El orden importa más que el contenido.
Primero, revocá. Antes de investigar, antes de armar el canal de incidente, antes de saber el alcance. La investigación consume horas y la credencial sigue sirviendo durante todas ellas. Si el impacto de revocar es un outage, ese es el hallazgo del post-mortem: el sistema no tolera perder una credencial.
Segundo, rotá y verificá los consumidores. La rotación termina cuando el último consumidor está usando el valor nuevo y el viejo está muerto, no cuando el gestor muestra la versión nueva. Acordate del connection pool.
Tercero, revisá el audit log desde el momento del commit, no desde el momento de la detección. La ventana de investigación arranca cuando el secreto quedó expuesto. En Codecov eso fueron dos meses de diferencia. Buscá uso desde IPs y regiones que no correspondan, llamadas fuera del horario del pipeline, y enumeración de recursos.
Cuarto, limpiá la historia. git filter-repo o BFG, coordinado con quienes tengan forks, sabiendo que los objetos desreferenciados pueden seguir accesibles por SHA. Es higiene, y va después de rotar.
Quinto, cerrá la ruta de propagación. Un secreto que se filtró por un log de CI se vuelve a filtrar por el mismo log si el step no cambia. La acción de cierre es el mecanismo que impide la repetición: push protection activado, la variable movida a OIDC, el ENV sacado del Dockerfile, el masking reemplazado por no imprimir el valor.
Un secreto que no expira solo es una deuda cuyo vencimiento lo decide otro.
Para seguir
Lecturas
- The State of Secrets Sprawl 2026 — El dataset que sostiene el eje: 28,65 millones de secretos nuevos filtrados en GitHub público en 2025, y más del 64% de las credenciales confirmadas válidas en 2022 seguían válidas en enero de 2026.
- How Bad Can It Git? Characterizing Secret Leakage in Public GitHub Repositories — El paper de referencia (NDSS 2019). Mide cuánto sobreviven los secretos en el historial de git después de ser borrados, y por qué el firehose de commits te encuentra antes que tu code review.
- Thanksgiving 2023 security incident — Cloudflare rotó miles de credenciales después del breach de Okta y dejó cuatro afuera por creerlas sin uso. Es el post-mortem que muestra qué cuesta un inventario de consumidores incompleto.
- OpenID Connect (security hardening your deployments) — GitHub Actions — La documentación oficial del mecanismo, incluida la parte que decide todo: cómo se acota el subject claim para que la confianza no sea 'cualquier workflow de mi organización'.
- Temporary security credentials in IAM — El lado del cloud: las credenciales temporales duran de 15 minutos a 12 horas, no se almacenan con el usuario y no hay que rotarlas porque dejan de servir solas.
- Secrets Management Cheat Sheet — La checklist neutral de proveedor sobre ciclo de vida y modelo de amenaza, incluidas las vías por las que se fugan las variables de entorno.
Videos
- Destroying Long-Lived Cloud Credentials with Workload Identity Federation — La tesis del artículo en formato charla, con el detalle de dónde se rompe la federación: la relación de confianza y el subject claim.
- Turtles All the Way Down: Managing Kubernetes Secrets — La mejor explicación del problema de bootstrapping y del modelo de amenaza real de los Secrets de Kubernetes.
- How to get dynamic with secrets management — El salto de secreto estático a secreto dinámico. Es de vendor: usalo por el concepto, no por el producto.