Stop leaking secrets in CI logs: masking and OIDC
Mask variables, avoid echoing secrets, and move to short-lived OIDC tokens so a leaked log isn't a breach.
CI is where credentials go to die loudly: echoed into job logs, baked into cached layers, exposed to fork pull requests, and copied into set -x traces. The fix is not one checkbox — it is layered hygiene. Mask variables so known patterns redact in logs, protect them so only trusted branches see them, stop shell tracing before secrets expand, and where the platform allows, delete the static secret entirely with OIDC federation to cloud IAM.
You will configure masked and protected variables correctly, audit scripts for trace and CLI-arg leaks, block untrusted pipelines from production credentials, and replace stored AWS or GCP keys with short-lived tokens minted per job. The same identity patterns power keyless Cosign signing — covered end to end in Software supply chain security.
Masking hides secrets in logs; it does not stop a malicious job from reading them. OIDC removes the secret the job could steal.
Mask and protect variables
GitLab masks values that meet complexity rules — but only in job logs, not in artifacts a job uploads. GitHub masks secrets registered in repo or environment settings. Protected (GitLab) or environment-scoped (GitHub) variables should only flow to pipelines on protected branches and tags, never to arbitrary feature branches or fork workflows without approval.
# Project -> Settings -> CI/CD -> Variables# API_TOKEN [x] Mask variable [x] Protect variable# AWS_ROLE_ARN (plain) — used with OIDC, not a secret keydeploy_prod:rules:- if: $CI_COMMIT_BRANCH == "main"
echo "token is $API_TOKEN"token is [MASKED]curl -H "Auth: $API_TOKEN" https://api.example.com still sends the real valuemasking ≠ the job cannot use the secretStop echoing secrets
Shell trace (set -x) prints every expanded command — secrets included — straight into the log. Never enable it in jobs that touch credentials. Avoid passing secrets as CLI arguments; they appear in /proc, process listings, and some debug output. Read secrets from files or env vars inside the tool, and mark generated artifacts as sensitive when they might contain echoes. Treat job logs and uploaded artifacts with the same retention and access controls as production audit trails.
The real fix: no static secret
A secret that does not exist cannot leak from a log. OIDC lets the pipeline present a signed JWT to AWS, GCP, Azure, or your registry; the cloud platform returns a short-lived credential bound to that workflow, branch, and repository. Rotate becomes unnecessary because nothing long-lived was stored.
permissions:id-token: writecontents: readjobs:deploy:runs-on: ubuntu-lateststeps:- uses: aws-actions/configure-aws-credentials@v4with:role-to-assume: arn:aws:iam::111122223333:role/github-deployaws-region: eu-west-1
Where this goes next
Clean secret handling unlocks keyless artifact signing — Cosign trusts the same OIDC identity your deploy job uses. Pair federated cloud access with gitleaks in CI, protected branches, and signed SBOM attestations so credentials and artifacts both have provenance. Software supply chain security walks secret hygiene through signing and admission verification.
Go deeper in a courseSoftware supply chain securitySecret hygiene, OIDC, signing, provenance, and policy gates across the pipeline.View course