GitHub Actions workflows should follow the principle of least privilege by providing jobs and steps with access only to the specific secrets they
require. When the entire secrets
context is made available using toJson(secrets)
or similar functions, it exposes all
repository secrets to the job or step, creating unnecessary security risks.
This practice increases the attack surface and potential for secret exposure, as any vulnerability in the job or step could compromise all secrets
rather than just the ones actually needed.
Ask Yourself Whether
- The job or step truly needs access to all secrets in the repository.
- There are secrets in the repository that are unrelated to the current job’s purpose.
- The job or step could function with access to only specific, named secrets.
- The workflow runs on external infrastructure where broader secret exposure increases risk.
There is a risk if you answer yes to any of the above questions.
Recommended Secure Coding Practices
- Provide jobs and steps with access only to the specific secrets they require.
- Use individual secret references instead of the full secrets context.
- Regularly audit and remove unused secrets from the repository.
- Consider using environment-specific secrets or secret scoping when possible.
- Implement proper secret rotation policies to limit exposure duration.
Sensitive Code Example
name: Example
on:
pull_request:
branches: [ main ]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Example Step
env:
SECRETS: ${{ toJson(secrets) }} # Sensitive
run: |
example-command "$SECRETS"
Compliant Solution
name: Example
on:
pull_request:
branches: [ main ]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Example Step
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
example-command "$API_KEY"
See