Choose GitHub Actions workflows by matching each job’s permissions and trust level to the code it handles, then test the configurations your project supports and put appropriate controls around deployments. Start with read-only GITHUB_TOKEN permissions, pin third-party actions to full commit SHAs, keep untrusted pull-request code away from privileged jobs, and use protected environments and narrowly scoped OIDC trust for cloud deployment where supported.
How to structure a workflow around its risk
A GitHub Actions workflow is a YAML-configured process made up of jobs. Jobs run in parallel by default; use dependencies such as needs when one job must wait for another. A practical design separates work by purpose and access: run build and test jobs with only the permissions they need, then make deployment depend on those checks.
Think of each job as a trust boundary. Ask what code it executes, whether that code comes from a trusted branch or an outside contributor, what secrets it can access, and what its token can change. Give a job only the access needed to do its own work, rather than granting workflow-wide permissions for convenience. See GitHub’s workflow syntax reference and guidance on secure use.
How to secure GitHub Actions
Limit token permissions and action access
Set GITHUB_TOKEN permissions explicitly at the workflow or job level, granting only the scopes required. GitHub recommends read-only default permissions for repository contents. A job that only checks out and tests code usually has no reason to receive write access to repository contents or other resources.
#1 Best Overall
Third-party actions and reusable workflows run as code within the job’s security context. Review their source and dependencies, and pin actions to a full-length commit SHA when you need an immutable reference. A tag is easier to read, but it can move; a full SHA identifies a specific revision.
Keep untrusted pull requests out of privileged execution
Do not use privileged trigger contexts to check out or execute untrusted pull-request code with elevated access. In particular, combining pull_request_target or workflow_run with a checkout and processing of contributor-controlled content can expose secrets or write-capable tokens. If a workflow genuinely needs a privileged step, design a clear boundary so untrusted code is not run in the privileged job.
Scope secrets to the jobs that need them. Automatic log redaction is not guaranteed to catch every transformed representation of a secret, so avoid printing credentials or derived values to logs.
How broadly to test
Build a matrix around supported configurations
A matrix creates a job for every configured combination, such as operating system and language runtime version. Include combinations that reflect the configurations you claim to support or that meaningfully reduce compatibility risk; do not multiply jobs simply because the matrix can. More combinations improve coverage of those configurations but also consume more run time and runner capacity.
Use job dependencies to make checks a deployment gate. For example, let build and test jobs run in parallel, then configure the deployment job to require both to succeed. This makes the intended sequence explicit without slowing independent checks. GitHub documents matrices in Running variations of jobs in a workflow and job dependencies in Using jobs in a workflow.
When to use caches and artifacts
| Use | Best for | Main consideration |
|---|---|---|
| Dependency cache | Regenerable dependencies and intermediate files reused across runs | Cache contents can be read and restored by workflows with access; never put secrets, tokens, or credentials in cached paths. |
| Workflow artifact | Outputs to retain or pass between jobs, such as test reports, screenshots, binaries, or logs | Artifacts preserve run outputs rather than acting as a reusable dependency cache. |
These features are not interchangeable. Treat restored cache files as untrusted because they can affect later execution, and restrict cache writes to trusted workflows. GitHub’s cache reference describes access modes including read, write, write-only, and none; allowing writes from low-trust triggers can reintroduce cache-poisoning risk. Consult GitHub’s dependency caching overview, cache reference, and workflow artifacts guide.
Rank #4
How to deploy safely
Gate environments according to the target
Model targets such as staging and production as GitHub environments. Depending on configuration, environment protection rules can require approval, restrict branches or tags, delay a job, or invoke custom protection rules. Environment secrets become available to a job that references the environment only after its required protection rules pass. Availability of environment secrets varies with repository visibility and GitHub plan, so check the current limits before relying on them.
Use controls proportionate to the deployment: an automatic staging deployment may suit a team’s workflow, while production may warrant branch restrictions or an approval gate. If overlapping runs could deploy to the same target at once, use a concurrency group so only one job or workflow in that group runs at a time; choose the group to match how your repository manages deployments.
Best Value
Prefer OIDC to long-lived cloud secrets when available
For cloud deployments, OpenID Connect (OIDC) can replace long-lived cloud credentials stored as GitHub secrets. A workflow requests a JWT from GitHub, and the cloud provider exchanges it for short-lived credentials if the provider’s trust policy accepts the request. Configure that policy to constrain which repository, ref, environment, or workflow identity may obtain credentials; the exact setup depends on the cloud provider.
The workflow needs id-token: write to request the JWT. That permission does not itself grant permission to change cloud resources: access comes from the cloud role and its trust policy. As GitHub’s OIDC guidance puts it, “Setting id-token: write in the workflow’s permissions does not give the workflow permission to modify or write to any resources.”
For environment rules and deployment configuration, see GitHub’s deployment controls guide and deployments and environments reference.
Quick Recap
A practical decision checklist
- Trust boundary: Identify whether a job processes forked or otherwise untrusted code, then decide what token permissions and secrets it can safely receive.
- Test scope: Include operating systems and runtime versions that match the project’s support promise; add matrix combinations only when their coverage justifies their cost and time.
- Credentials: Prefer short-lived OIDC credentials with restrictive provider trust conditions to long-lived stored cloud credentials when the provider supports it.
- Deployment control: Choose between automatic promotion, branch or tag restrictions, approvals, and other environment rules based on the target’s risk.
- Data handling: Cache regenerable files, retain or transfer outputs as artifacts, and never place secrets in cache contents.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Recommended Free Tools




