| draft | false | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| title | Securing GitHub Actions Workflows | ||||||||||
| publishDate | 2024-08-16 | ||||||||||
| params |
|
||||||||||
| pillars |
|
||||||||||
| areas |
|
||||||||||
| verticals |
|
||||||||||
| personas |
|
||||||||||
| platform |
|
||||||||||
| features |
|
||||||||||
| components |
|
||||||||||
| github |
|
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.
To secure your GitHub Actions workflows, consider the following strategies:
- 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.
- 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.
- 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.
- 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. Requiring the use of a commit hash can also be enforced via check box through the actions and reusable workflows policy. This can be enforced at the enterprise, organization, or repository level.
- 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.
- 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.
- 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.
- Use actions policy to restrict which actions can be used: The allowed actions and reusable workflows policy enables you to restrict which actions can be executed within an enterprise, organization, or repository. Additionally, this policy allows you to block the use of any specific action. For example, if a compromised action is identified, you can easily block it without needing to search for its usage across your repositories.
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.
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.
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@v5Dependabot 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.
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.7Including 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.
Security administrators can enforce policies that require all GitHub Actions workflows and reusable workflow references to be pinned to full-length commit SHAs. This enforcement is available at the enterprise, organization, and repository levels. Administrators can enable this requirement using a dedicated checkbox in the allowed actions and reusable workflows policy settings.
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.
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.
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.
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.
{{< callout type="info" >}} 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. {{< /callout >}}
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-details %}}
{{% related-links-github-docs %}}
Specifically, you may find the following links helpful: