GitHub Actions security hardening checklist: permissions, SHA pinning, and PR event traps
securityopsweb

GitHub Actions security hardening checklist: permissions, SHA pinning, and PR event traps

3 min read

A practical checklist to harden GitHub Actions as part of your software supply chain. Focus on least-privilege permissions, pinning third-party Actions by SHA, safe handling of fork PRs, and deployment gates.

Table of Contents

How do you harden GitHub Actions so CI doesn’t become a supply chain attack path?

Conclusion

Treat GitHub Actions as an execution environment that concentrates permissions and secrets. The fastest wins are:

  1. set least-privilege permissions explicitly
  2. pin third-party Actions by commit SHA
  3. avoid mixing base-branch privileges with untrusted code (fork PRs, pull_request_target)
  4. gate deployments with Environments + required reviewers

Do these before you optimize build speed.

Explanation

CI incidents are usually a combination of:

  • supply chain (Actions/npm/Docker)
  • untrusted code paths (fork PRs)
  • excessive permissions (write tokens, secrets exposure)

The real risk is not “a secret leaked once”. It’s CI becoming a pivot that:

  • modifies your repo
  • pushes malicious releases
  • deploys to production

Practical Guide

Step 1: set workflow/job permissions explicitly (highest ROI)

Do not rely on defaults. Start with read-only.

permissions:
  contents: read

Only widen for specific jobs. Example: PR comments need write to pull-requests.

permissions:
  contents: read
  pull-requests: write

Decision rule:

  • “write because convenient” is how repos get popped.

Step 2: pin third-party Actions by commit SHA

Do:

- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

Avoid:

- uses: actions/checkout@v4

Decision rule:

  • If the reference can move, it must be treated like a dependency update PR.

Step 3: handle fork PRs safely (and avoid pull_request_target by default)

Safer baseline:

  • use pull_request for tests
  • do not expose secrets to fork PRs

If you must use pull_request_target:

  • do not execute PR code
  • do not pass secrets
  • keep it read-only (labels/comments)

Step 4: design workflows that don’t need long-lived secrets

  • avoid secrets in jobs that run on untrusted input
  • prefer OIDC (short-lived tokens) for cloud deploys where possible

Step 5: gate deployments with Environments

  • set environment: production
  • require reviewers

This prevents silent CI-to-prod pushes.

Step 6: treat self-hosted runners as high blast radius

Minimum bar:

  • prefer ephemeral runners
  • avoid storing secrets on runners
  • don’t share workspaces/caches across trust boundaries

Pitfalls

  • broad default GITHUB_TOKEN permissions
  • tags instead of SHAs for Actions
  • pull_request_target running with secrets + executing PR code
  • self-hosted runners that retain state after compromise
  • cache key design that enables cache poisoning

Checklist

  • [ ] Workflow/job permissions are explicitly set (least privilege)
  • [ ] Only specific jobs have write permissions (PR comments, releases)
  • [ ] Third-party Actions are pinned by commit SHA
  • [ ] Pin updates are batched and reviewed (weekly PR)
  • [ ] Fork PR workflows run without secrets
  • [ ] pull_request_target is avoided, or isolated (no PR code execution)
  • [ ] Deploy jobs use OIDC where possible (short-lived creds)
  • [ ] Deployments are gated via Environments + required reviewers
  • [ ] Self-hosted runners are ephemeral or treated as sensitive infrastructure
  • [ ] Cache keys are reviewed to reduce poisoning risk
  • [ ] Incident response path exists (token rotation, workflow disable, runner quarantine)

FAQ

Q1. Is pinning Actions by SHA really necessary?

Yes. Tags move. SHAs don’t. Pinning turns workflow dependency drift into a reviewable diff.

Q2. Can I safely use pull_request_target?

Only if you isolate it completely: no secrets and no execution of PR code. Otherwise it’s a common foot-gun.

Q3. What’s the single highest-ROI hardening step?

Explicit permissions with least privilege. Over-privileged tokens are the fastest path to repo compromise.

References

Popular

  1. 1Permit2 explained (Web3): why approvals changed and how to use it safely (checklist)
  2. 2Read wallet signing screens (Web3): a 30-second checklist to avoid permission traps
  3. 3Spec-to-implementation prompt template (AI development): how to stop the model from guessing
  4. 4Revoke token approvals on EVM: how to audit allowances safely (checklist)
  5. 5Clarifying questions checklist (AI development): what to ask before you let an LLM build

Related Articles