GitHub Actions Security: Hardening CI Pipelines That Ship Production
GitHub Actions Security: Hardening CI Pipelines That Ship Production
CI pipelines concentrate secrets, cloud credentials, and deployment rights in a single place. A compromised workflow can publish code, leak tokens, or push directly to production. GitHub documents the mechanics in security for GitHub Actions, and the OWASP CI/CD Top 10 categories what to prioritize. The checklist below operationalizes both.
1. Pin Actions by Tag or SHA
Reference third-party actions by a full version tag or commit SHA, never by branch or floating minor. A branch reference looks harmless until the branch moves to attacker-controlled code. Pinning is the single easiest supply-chain control a team can add.
2. Constrain the GITHUB_TOKEN by Default
Set permissions: at the workflow top and drop to contents: read unless a job demonstrably needs more. A workflow that builds a container and pushes an image should never hold contents: write plus packages: write plus id-token: write silently — each permission is a capability an attacker inherits from a single workflow run.
3. Prefer OIDC Over Stored Cloud Keys
Replace long-lived AWS/GCP/Azure credentials in CI with OpenID Connect federation: the workflow exchanges its OIDC identity for short-lived cloud tokens. This removes a static AWS_ACCESS_KEY_ID from being the crown jewel of the pipeline — and from appearing in any compromised log output.
4. Keep Secrets Out of Logs — and Refuse Untrusted Triggers
- Secrets passed through
echoin debug mode,--verboseCLI flags, or job summaries leak in aggregated log archives. Audit workflow templates for unsafe interpolation. - Never run workflows on
pull_request_targetwith checkout of the PR's code. That trigger combines untrusted code with write-capable tokens — the classic pipeline poisoning path.
Roll Out in Four Passes
Start with deploy pipelines: OIDC, deny-by-default permissions, and pinned actions cover most of the risk for the highest-value workflows. Then sweep the remaining templates with the same rules. Third, eliminate pull_request_target checkouts and add a CI check that fails a pull request when an action reference moves. Finally, put runner isolation on the roadmap — self-hosted runners should be ephemeral single-job machines so a compromised job cannot linger and pollute the next run.
Three Mistakes That Keep Appearing
One pattern repeats across reviews. Teams pull actions/checkout or a popular community action from master/main, so an attacker who compromises the branch ships code into the pipeline overnight. Then permissions: is declared only at workflow level and forgotten at job level, letting every job inherit the full token. And secrets are written as plaintext env variables instead of through the Secrets store. All three slip past review and become incidents. The first review catches them on paper; the second catches them on a breach timeline.
What a Review Looks Like
An Actions review walks the workflow tree branch by branch: trigger surface, token scope, action pinning, OIDC adoption, secret flows, and self-hosted runner isolation. Every workflow that can deploy or mutate production infrastructure gets the strictest treatment — because that is where the tokens are worth stealing.
Sources
AI Security Starter Training
Request a practical checklist for prompt injection, RAG data leakage, MCP risks, and model-file security before launch.
Related Services
Scope Estimator
Get a rough engagement size before the scoping call.
- What this depth covers
- Full coverage of the OWASP-family test plan for the in-scope targets: broken access control, injection, authn and authz flaws, business-logic abuse, findings with reproduction steps.
- What it does not cover
- Multi-week adversary emulation, physical and phishing vectors, custom exploit development, source-code review.
Indicative effort
~5 tester-days
Estimate only, not a quote. It scales with the three factors above and assumes the targets are reachable and test accounts are provided in the test window.