Securing GitHub Actions Workflows

Scenario overview

Continuous integration and continuous delivery (CI/CD) are essential components of modern software development. GitHub Actions is a powerful tool that enables developers to automate repetitive tasks, and reduce the risk of human error in manual workflows.

However, CI/CD tools at their core offer remote code execution as a service. This makes them a prime attack vector for malicious actors. Securing GitHub Actions workflows is essential to prevent unauthorized access to your codebase, and to protect your organization from potential security breaches.

Key design strategies and checklist

To secure your GitHub Actions workflows, consider the following strategies:

  1. Use OpenID Connect (OIDC) for authentication: Use OIDC to establish authentication between GitHub Actions and any downstream systems or cloud providers. This ensures that only authorized users can access those downstream resources without the need for long-lived credentials stored as secrets.
  2. Implement least privilege for workflow permissions: Limit the permissions granted to GitHub Actions workflows and jobs to the minimum required to perform their tasks. This reduces the risk of privilege escalation and unauthorized access to sensitive resources.
  3. Use Dependabot to upgrade vulnerable Actions: Dependabot can help you identify and remediate vulnerable dependencies in your Actions workflows. Regularly review and update your dependencies to ensure that you are not using outdated or insecure third-party Actions.
  4. Pin versions of Actions: Pin the commit hash of Actions used in your workflows to ensure that you are not affected by breaking changes or security vulnerabilities in newer versions.
  5. Avoid “unpinnable” Actions: Avoid using Actions that include unpinned dependencies or that pull in code from external sources without verification. This can introduce security risks and make your workflows vulnerable to supply chain attacks.
  6. Avoid workflow injection: Ensure that your workflows are not vulnerable to injection attacks by sanitizing user input and avoiding the use of dynamic values in sensitive contexts.
  7. Use caution with public repositories: Given that anyone on the internet can suggest changes to a public repository, it is important to use caution with regard to the workflow triggers and runners that you use.

Assumptions and preconditions

This article assumes that you are familiar with GitHub Actions and have experience creating and managing workflows. It also assumes that you have a basic understanding of security best practices and are familiar with concepts such as authentication and authorization.

Recommended deployment

Use OpenID Connect (OIDC) for authentication

CI/CD platforms like GitHub Actions often require access to sensitive resources such as source code repositories, build artifacts, and deployment environments. To ensure that only authorized users and services can access these resources, you should use OpenID Connect (OIDC) for authentication.

The benefit of OIDC over a more traditional approach of storing secrets as secret variables is that it eliminates the need for long-lived credentials. In turn, there is no longer a risk of a compromised account exfiltrating secrets from your CI/CD environment.

By establishing trust between GitHub Actions and a cloud provider that supports OIDC, attributes like the GitHub organization, repository, workflow, or user can be used to approve or deny access to cloud resources. This provides both a more granular and more secure level of control over who can access what resources, and under what conditions.

In order to scale to many organizations and repositories, OIDC can be implemented with reusable workflows to ensure that only trusted, centralized workflows can authenticate and view or modify sensitive resources.

Implement least privilege for workflow permissions

GitHub Actions workflows include a pre-defined GITHUB_TOKEN variable that grants default permissions to the jobs in the workflow. The default permissions can be set to permissive or restricted at the organization level or at the repository level.

Additional permissions can be granted to the GITHUB_TOKEN by using the permissions parameter at the top of the workflow for all jobs, or in the jobs.<job_id>.permissions section of the workflow file for that particular job. By specifying the required permissions for each job, you can limit the scope of the GITHUB_TOKEN and reduce the risk of privilege escalation.

For example, you can set the permissions for the GITHUB_TOKEN variable across all jobs in the workflow by adding the following code snippet at the top of the workflow file:

name: "My workflow"

on: [ push ]

permissions:
  contents: read
  security-events: read
  pull-requests: write

jobs:
  ...

For more granular control, you can set the permissions for a specific job by adding the following code snippet to the job definition:

name: "My workflow"

on: [ push ]

jobs:
  stale:
    runs-on: ubuntu-latest

    permissions:
      issues: write
      pull-requests: write

    steps:
      - uses: actions/stale@v5

Use Dependabot to upgrade vulnerable Actions

Dependabot is a GitHub feature that automatically identifies and creates pull requests to update outdated dependencies in your repositories. By enabling Dependabot for your repository, you can ensure that your Actions workflows are using the latest versions of dependencies and are not vulnerable to known security issues.

Pin versions of Actions

When using third-party Actions in your workflows, it is important to pin the Action to a specific commit hash. This ensures that your workflows are not affected by breaking changes or security vulnerabilities in newer versions of the Action, and protects you from supply chain attacks that target the third-party Actions that you use.

Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the Action’s repository, as they would need to generate a SHA-1 collision for a valid Git object payload. When selecting a hash, you should verify it is from the Action’s repository and not a repository fork.

To pin the version of an Action, you can specify the commit hash in the uses field of the workflow file. For example:

- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

Including the version number in a comment can help you and others keep track of the version you are using, and makes it easier to spot when an outdated action is in use. It also enables Dependabot Security Updates to recommend updates to the Action when a newer, secure version is available.

Avoid “unpinnable” Actions

Some Actions include unpinned dependencies or pull in code from external sources without verification. These Actions are considered “unpinnable” because even after pinning to a specific commit hash, they include dynamic components that can change the behavior of the Action without changing the source code for the Action. Using unpinnable Actions in your workflows can introduce security risks and make your workflows vulnerable to supply chain attacks.

To avoid using unpinnable Actions, you should carefully review the source code of the Actions you are using and ensure that they do not include elements such as unpinned container images, unpinned composite Actions, or scripts that download code from external sources without verification.

Avoid workflow injection

When creating workflows, custom actions, and composite actions, you should always consider whether your code might execute untrusted input from attackers. This can occur when an attacker adds malicious commands and scripts to a context. When your workflow runs, those strings might be interpreted as code which is then executed on the runner.

You can find more details around the risk of workflow script injections and how to mitigate those risks in the GitHub Docs here.

Use caution with public repositories

Public repositories are a great way to enable open source collaboration, but they can also introduce risks if certain workflow triggers are attached to their Actions workflows. For example, the pull_request_target trigger runs when a pull request is opened or reopened or when the head branch of the pull request is updated. When combined with an explicit checkout of an untrusted pull request, this can lead to a compromise of the repository’s contents and secrets, otherwise referred to as a “pwn request.” For more details on how to prevent pwn requests, see the GitHub Security Lab article here.

Another risk related to public repositories is the use of static self-hosted runners. When a public repository is configured to use self-hosted runners, forks of your public repository can potentially run dangerous code on your self-hosted runner machine by creating a pull request that executes the code in a workflow. This can lead to many risks such as:

  • Malicious programs running on the machine.
  • Escaping the machine’s runner sandbox.
  • Exposing access to the machine’s network environment.
  • Persisting unwanted or dangerous data on the machine.
ℹ️
This is not an issue with GitHub-hosted runners because each GitHub-hosted runner is always a clean isolated virtual machine, and it is destroyed at the end of the job execution.

Additional solution detail and trade-offs to consider

Pinning Actions based on a version tag

Although pinning to a commit hash is the most secure option, specifying a tag is more convenient and is widely used. If you’d like to specify a tag, then be sure that you trust the Action’s creators. The ‘Verified creator’ badge on GitHub Marketplace is a useful signal, as it indicates that the Action was written by a team whose identity has been verified by GitHub. Note that there is risk to this approach even if you trust the author, because a tag can be moved or deleted if a bad actor gains access to the repository storing the Action.

Seeking further assistance

GitHub Support

Visit the GitHub Support Portal for a comprehensive collection of articles, tutorials, and guides on using GitHub features and services.

Can’t find what you’re looking for? You can contact GitHub Support by opening a ticket.

GitHub Expert Services

GitHub’s Expert Services Team is here to help you architect, implement, and optimize a solution that meets your unique needs. Contact us to learn more about how we can help you.

GitHub Partners

GitHub partners with the world’s leading technology and service providers to help our customers achieve their end-to-end business objectives. Find a GitHub Partner that can help you with your specific needs here.

GitHub Community

Join the GitHub Community Forum to ask questions, share knowledge, and connect with other GitHub users. It’s a great place to get advice and solutions from experienced users.

Related links

GitHub Documentation

For more details about GitHub’s features and services, check out GitHub Documentation.

Specifically, you may find the following links helpful:

External Resources