Excessive Secret Exposure
Description
The secrets context can be serialized in bulk with the toJson(secrets) expression. Doing so passes every secret available to the workflow into a single value, exposing credentials to a step that should only ever need one or two. 1 If that step runs a third-party action, logs verbosely, crashes with a stack trace, or is itself compromised, the entire secret store is at risk instead of a single credential. This pattern also defeats GitHub’s per-value log masking, because a serialized blob may be transformed (encoded, split, or reformatted) before it reaches the log. Related issues are covered in Secret in Environment and Secrets Access Untrusted.
Vulnerable Instance
- A step uses
${{ toJson(secrets) }}in aruncommand, anenv:value, or awith:parameter. - All repository, environment, and organization secrets visible to the workflow are exposed to that step.
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Configure
uses: third-party/configure@v1
env:
ALL_SECRETS: ${{ toJson(secrets) }} # exposes every secret at onceMitigation Strategies
Pass only the secrets a step needs, by name Reference individual secrets explicitly so each step receives the minimum set of credentials.
- name: Configure uses: third-party/configure@v1 env: API_TOKEN: ${{ secrets.API_TOKEN }}Never use
toJson(secrets)There is no safe production use for serializing the whole secrets context. If you are tempted to use it for debugging, do not — it will leak credentials into logs and artifacts.Prefer short-lived credentials Use OpenID Connect (OIDC) to obtain short-lived, identity-bound tokens instead of storing and forwarding long-lived secrets. See Long Term Cloud Credentials.
Scope secrets to environments Store sensitive secrets at the environment level with required reviewers so they are only available to protected deployment jobs.
Secure Version
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Configure
uses: third-party/configure@v1
env:
- ALL_SECRETS: ${{ toJson(secrets) }}
+ API_TOKEN: ${{ secrets.API_TOKEN }}
Impact
| Dimension | Severity | Notes |
|---|---|---|
| Likelihood | Usually introduced as a debugging shortcut or a misunderstanding of how to pass secrets to an action. | |
| Risk | A single leak, crash, or compromised action exposes the workflow’s entire secret store rather than one credential. | |
| Blast radius | All repository, environment, and accessible organization secrets are exposed simultaneously. |
References
- GitHub Docs, “Security hardening for GitHub Actions — Using secrets,” https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-secrets 1
- GitHub Docs, “Using secrets in GitHub Actions,” https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions 2
GitHub Docs, “Security hardening for GitHub Actions,” https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions ↩︎ ↩︎
GitHub Docs, “Using secrets in GitHub Actions,” https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions ↩︎