principlebashCritical
Secrets in CI: never echo, always mask
Viewed 0 times
secretsmasksecurityenv varsadd-masktoken exposure
Problem
Secrets accidentally get printed to CI logs via debug statements, error output, or shell expansion errors. Once printed, the secret is exposed in the build log even if the run is later deleted—logs may have already been scraped.
Solution
Never use
Never do:
echo $SECRET in CI. Pass secrets via environment variables, never as positional CLI args. Use ::add-mask:: for dynamic values that aren't stored as GitHub secrets:- name: Mask derived token
run: echo "::add-mask::${{ steps.get-token.outputs.token }}"
- name: Use secret safely
env:
API_KEY: ${{ secrets.API_KEY }}
run: curl -H "Authorization: Bearer $API_KEY" https://api.example.comNever do:
run: curl -H "Authorization: Bearer ${{ secrets.API_KEY }}"—this embeds the value in the workflow YAML before the shell sees it.Why
GitHub Actions redacts known secrets from logs, but only those registered as secrets. Inline
${{ secrets.X }} in a run block gets interpolated into the shell command string, where a verbose command or errexit trace can print it.Gotchas
- Secrets are not passed to workflows triggered by forks—environment variables will be empty strings
- add-mask only applies to subsequent log lines in the same job, not previous ones
- set -x (xtrace) will print all env vars including secrets—never enable xtrace in production CI jobs
Revisions (0)
No revisions yet.