BlogCI/CD

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.

Aug 27, 2025·9 min readIntermediate

CI is where secrets go to leak: into logs, into cached layers, into forked-PR builds. The defense is layered — mask variables so they never print, stop shell tracing from echoing them, and where you can, delete the static secret entirely by federating with OIDC.

bash — masking in actionlive
echo "token is $API_TOKEN"
token is [MASKED]
GitLab masks values that meet its complexity rules — but only in logs

Mask and protect

.gitlab-ci.yml
# Project -> Settings -> CI/CD -> Variables
# API_TOKEN [x] Masked [x] Protected
# Masked = redacted in job logs
# Protected = only exposed on protected branches/tags

Stop echoing secrets

set -x is a secret exfil tool
Shell trace prints every expanded command, secrets included, straight into the log. Never enable it in jobs that touch credentials, and avoid passing secrets as CLI args (they show up in ps and traces).

The real fix: no static secret

A secret that does not exist cannot leak. OIDC lets the pipeline exchange a short-lived, workload-bound token for cloud access — no long-lived key stored anywhere.

.github/workflows/deploy.yml
permissions:
id-token: write # request an OIDC token
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111:role/deploy
aws-region: eu-west-1
Go deeper in a courseSoftware supply chain securitySecret hygiene, OIDC, signing, and provenance across the pipeline.View course

Related posts