Skip to content

Guardrail: No Hardcoded Secrets in Workflow Files

Rule: Never place secrets, tokens, passwords, connection strings, or credentials as literal values in any GitHub Actions workflow env block, with parameter, or run script. Always reference them through the secrets context.


Why This Matters

Workflow files are committed to source control and visible to anyone with read access to the repository. A hardcoded secret in a workflow file is a leaked secret — it persists in Git history even after removal and can be harvested by automated scanners.


Bad Examples

# ❌ BAD — password as a literal value in env
env:
  DB_PASSWORD: "my-password-123"
  API_KEY: "sk-abc123def456"
# ❌ BAD — secret passed inline to an action input
- uses: azure/login@v2
  with:
    client-secret: "s3cret-value-here"
# ❌ BAD — connection string embedded in a run step
- run: |
    az sql db connect --connection-string "Server=myserver;Password=hunter2"

Good Examples

# ✅ GOOD — reference secrets context
env:
  DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
  API_KEY: ${{ secrets.API_KEY }}
# ✅ GOOD — secrets passed through action inputs
- uses: azure/login@v2
  with:
    client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
# ✅ GOOD — secret referenced in a run step
- run: |
    az sql db connect --connection-string "${{ secrets.SQL_CONNECTION_STRING }}"

How to Audit Existing Workflows

  1. Search for literal strings in env blocks:

    grep -rn 'env:' .github/workflows/ -A 10 | grep -vE '\$\{\{.*secrets\.' | grep -E ':\s*".+"'
    

  2. Look for with: parameters that don't use secrets context:

    grep -rn 'with:' .github/workflows/ -A 10 | grep -vE '\$\{\{.*secrets\.' | grep -iE '(password|secret|token|key|credential|connection).*:\s*".+"'
    

  3. Review run: blocks for inline credentials:

    grep -rn 'run:' .github/workflows/ -A 5 | grep -iE '(password|secret|token|api.key)='
    

  4. Use GitHub's secret scanning: Enable secret scanning on the repository for automatic detection.


Remediation

If a hardcoded secret is found in a workflow file:

  1. Rotate the credential immediately — assume it is compromised.
  2. Add the value as a GitHub encrypted secret.
  3. Update the workflow to reference ${{ secrets.SECRET_NAME }}.
  4. Rewrite Git history to remove the secret from prior commits if feasible, or contact your security team.

References