Best practice · Security

GitHub Actions secrets: prevent leaks and overexposure

Expose a GitHub Actions secret only to the step that consumes it, gate production secrets with an environment, prefer short-lived OIDC credentials, and keep credentials out of logs, command arguments, caches, and artifacts.

How StarSling works

AI agents open the PR

StarSling agents inspect your workflow, apply this optimization, and open a reviewable PR automatically.

Do this: scope and gate the secret

Production credentials live in an environment whose protection rules are configured in repository settings, or are minted just in time with OIDC. A secret is mapped through env only on the step that needs it, never serialized from the whole secrets context, passed on a command line, cached, or included in a broad artifact upload. Reusable workflows receive an explicit secret allowlist instead of secrets: inherit.123

An environment-protected deploy with step-scoped secret input
permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    # Configure required reviewers or another deployment protection rule under
    # Settings > Environments > production. This key alone does not create a gate.
    environment: production
    steps:
      - uses: actions/checkout@v7
      - name: Deploy
        run: ./scripts/deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Avoid this: expose secrets to the whole job

Dependency scripts and unrelated steps can read the credential, while logs, process arguments, or a whole-workspace artifact can expose it beyond the intended deploy command.

Secrets exposed to every step and copied into output
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
      ALL_SECRETS: ${{ toJSON(secrets) }}
    steps:
      - uses: actions/checkout@v7
      - run: npm install
      - run: ./scripts/deploy.sh --token "$DEPLOY_TOKEN"
      - uses: actions/upload-artifact@v4
        with:
          name: workspace
          path: .

Secret masking is not a security boundary

GitHub redacts registered secret values in many logs, but transformed values and sensitive data that was never registered may not be masked. Prevent exposure at the source: never dump toJSON(secrets), keep secret-bearing files outside cached or uploaded paths, and use ::add-mask:: only as an additional log safeguard. The ci-secure skill checks the concrete leak paths it can prove.

Runs on your machine. GitHub API optional, for the impostor-SHA check and the dormancy note.

Prefer OIDC to long-lived cloud keys

When the provider supports it, exchange GitHub's OIDC token for a short-lived credential instead of storing a long-lived cloud key. Restrict the provider trust policy by repository, branch, and protected environment, and scope id-token: write to the deploy job only.

Why it matters

A secret is protected only until code in the job can read it. Third-party actions, dependency install scripts, shell tracing, broad artifacts, and shared caches all expand that reader set. Scoping and environment gates reduce who can trigger access; OIDC shortens how long a stolen credential remains useful.

When a workflow needs credentials

Use it when

Apply this to any release, publish, deploy, signing, or integration job that reads repository, organization, or environment secrets.

Be careful when

Do not treat masking as permission control, and do not move a secret to a repository variable or plain output. If a value must remain confidential, it stays a secret and reaches only its consumer.

Verify on your repo

Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.

Prompt for your coding agent
Audit .github/workflows for GitHub Actions secret exposure. Flag whole-context dumps, workflow- or job-wide secret environment variables, secrets passed in command arguments, `secrets: inherit`, credential files inside cache or artifact paths, and whole-workspace uploads. Move each secret to the consuming step's env, enumerate reusable-workflow secrets, narrow artifact and cache paths, protect production credentials with an environment, and prefer a short-lived OIDC credential when the provider supports it. Do not assume log masking prevents exposure. Preserve deploy behavior, show the diff, and open a PR rather than applying it blindly.

Ground these changes in the upstream docs before you edit: https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets, https://docs.github.com/en/actions/reference/security/secure-use, https://docs.github.com/en/actions/reference/security/oidc. If you cannot fetch them, say so rather than guessing, and cite what you used in the PR description.

Prefer to check by hand?

  1. Search workflows for toJSON(secrets), job- or workflow-level secret environment variables, secrets: inherit, and secrets interpolated directly into run: commands.

  2. Inspect cache and artifact paths for credential files, home directories, dotfiles, or whole-workspace uploads.

  3. Confirm production jobs reference an environment whose required reviewers or other deployment protection rules are actually configured in repository settings; the environment: key alone does not create an approval gate.

  4. Replace supported long-lived cloud credentials with narrowly trusted OIDC roles.

Go further

One fix, all of them, or forever.

You have the prompt for this one practice. Here is how much further you can take it, each step doing more for you than the last.

  1. Fix this one thing

    Copy the prompt above

    Hand P14.4 / P14.12 / P14.14 / P14.17 / P14.19 / P14.21 / P14.22 to your coding agent and fix it in your repo today.

  2. Fix everything, once

    Install the ci-secure skill

    One prompt checks every workflow against ten critical exploit-chain attack vectors, explains each reachable path, and lets you choose focused fixes. Open source, runs locally. It is not a comprehensive security audit.

  3. Keep it fixed, forever

    Install the StarSling GitHub App

    Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.

More best practices for GitHub Actions

Where to go next in the CI best-practices catalog.

All CI best practices

Trying to make GitHub Actions faster?

If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement.

FAQ

Are GitHub Actions secrets safe because logs mask them?

No. Redaction is a log safeguard, not an access boundary, and transformed or unregistered values may not be masked. Keep secrets away from code and outputs that do not need them.

Should I use repository or environment secrets?

Use an environment secret for production or release credentials, then configure required reviewers or another deployment protection rule for that environment in repository settings. The YAML environment: key selects the environment but does not create an approval gate by itself. Repository secrets fit lower-risk credentials needed across ordinary workflows, but should still be step-scoped.

Sources

1GitHub Actions · using secrets (opens in new tab)

2GitHub Actions · secure use reference (opens in new tab)

3GitHub Actions · OIDC reference (opens in new tab)

Last updated 2026-08-19