Hardening GitHub Actions: Five Changes Worth Making Today
Concrete, copy-pasteable security improvements for CI pipelines: OIDC over long-lived secrets, pinning, least-privilege tokens, and more.
CI pipelines are supply chain. Here are five changes that each take under an hour and meaningfully raise the bar. None of them require a paid plan.
1. Use OIDC instead of long-lived cloud credentials
Stop storing AWS_ACCESS_KEY_ID as a repo secret. Use OIDC to mint
short-lived role credentials per workflow run.
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111122223333:role/gh-actions-deploy
aws-region: eu-west-1
The corresponding trust policy on the role restricts who can assume it by repo and branch:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main"
}
}
}]
}
No key to rotate, no key to leak.
2. Pin third-party actions to a commit SHA
Tag @v4 can be moved. A SHA can’t.
# weak
- uses: actions/checkout@v4
# strong
- uses: actions/checkout@1d96dk77271ac93c91567be2a722c7d058b4510a
Keep a comment with the version next to each pin so upgrades are reviewable.
3. Scope GITHUB_TOKEN per job
The default token is broad. Set explicit permissions at the top of every
workflow, and narrower per job.
permissions:
contents: read
pull-requests: write
# everything else: none (default)
4. Require approval for external PRs
secrets are masked for first-time contributors by default, but pull_request_target
events run with write access. Avoid pull_request_target for building
untrusted code; use pull_request with no secrets.
5. Pin your runners and disable sudo
If you don’t need sudo, remove it. Self-hosted runners without ephemeral
cleanup are a common foothold — prefer GitHub-hosted unless you have a
reprovision-after-every-job story.
Closing
None of these make you “secure.” They remove the easy wins an attacker would take first. Do them this week, then go read your SBOM.