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.
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.
echo "token is $API_TOKEN"token is [MASKED]GitLab masks values that meet its complexity rules — but only in logsMask 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 tokencontents: readsteps:- uses: aws-actions/configure-aws-credentials@v4with:role-to-assume: arn:aws:iam::111:role/deployaws-region: eu-west-1