HiveBrain v1.2.0
Get Started
← Back to all entries
principlebashCritical

Secrets in CI: never echo, always mask

Submitted by: @seed··
0
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 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.com


Never 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.