Introduction
GitHub Actions has tons of different tools and resources that make it a powerful and convenient platform for developers. One of these is the GITHUB_TOKEN, the API token automatically generated by GitHub for each GitHub Action job run. All steps in the GitHub Action job can use this token to interact with GitHub services.
In this blog, we'll delve into the nuances of GitHub Tokens, their permissions, and best practices for ensuring secure and efficient workflow execution.
If you are looking for a solution to secure GITHUB_TOKEN across your organization, checkout the StepSecurity GitHub App. The App discovers all GitHub Action workflows with elevated GITHUB_TOKEN permissions and helps set the minimum permissions.
If you’re looking for more blog on GitHub Actions security, check out these previous blogs:
7 GitHub Actions Security Best Practices (With Checklist)
8 GitHub Actions Secrets Management Best Practices to Follow Practices
5 Effective Third-Party GitHub Actions Governance Best Practices
Pinning GitHub Actions for Enhanced Security: Everything You Should Know
What is GITHUB_TOKEN?
The GITHUB_TOKEN secret, is an automatically generated API token provided by GitHub for authentication within GitHub Actions workflows. This token serves as a means to interact with GitHub's APIs on behalf of GitHub Actions. When you enable GitHub Actions for a repository in your GitHub account, GitHub installs a GitHub App on the repository behind the scenes. GITHUB_TOKEN is actually a GitHub App installation access token. Each time a GitHub Actions job is run, GitHub generates a new installation access token for this app and injects it as GITHUB_TOKEN secret in the job runtime environment. The GitHUB_TOKEN expires when a job finishes or after 24 hours. As the app is installed on the repository, the token is authorized to call GitHub APIs only for the repository.
Using GITHUB_TOKEN in a GitHub Action Workflow
You can reference GITHUB_TOKEN in two ways inside your GitHub account.
As an Action secret
GitHub automatically creates an Action secret named GITHUB_TOKEN for all workflow runs. The example below shows how to access GITHUB_TOKEN as a secret:
From GitHub context
The GitHub context also provides a property named token that contains the GITHUB_TOKEN value for the run. The example below shows how to use GITHUB_TOKEN from the context.
Most GitHub dev tools such as GitHub CLI already know how to use GITHUB_TOKEN so you don’t need to do anything explict to make these tools work with GITHUB_TOKEN.
GITHUB_TOKEN vs Personal Access Token (PAT)
Personal access tokens are intended to access GitHub resources on behalf of the GitHub user for programmatic scenarios. As opposed to GITHUB_TOKEN, which is tied to a repository, PATs are tied to GitHub users. GITHUB_TOKEN lifecycle from generation to revocation is completely managed by GitHub. Whereas enterprises are responsible for managing PATs.
For GitHub Action, it’s recommended to use GITHUB_TOKEN wherever possible. As GITHUB_TOKEN is scoped to a repository, PAT (or GitHub Personal Access Token) is a solution in case you want a workflow to make GitHub API requests for another repository.
Key Security Risks with GITHUB_TOKEN
As GITHUB_TOKEN has permissions to make GitHub API requests, it can be misused to maliciously overwrite software releases and source code files. An adversary can compromise a build tool, dependency or Action to:
- Run malicious code in the Action workflow to make GitHub API calls.
- Exfiltrating GITHUB_TOKEN to a remote endpoint and using it outside of the runner.
All Actions can access the GITHUB_TOKEN secret by the GitHub context even if you don't explicitly pass the token to the Action.
GITHUB_TOKEN Permissions
The GITHUB_TOKEN permissions are limited only to your workflow-containing repository. However, it's essential to use the least privileged token permissions to ensure the security and efficiency of workflows. The token is authorized to call GitHub APIs for the following GitHub resources:
- actions
- checks
- contents
- deployments
- id-token
- issues
- metadata
- packages
- pages
- pull-requests
- repository-projects
- security-events
- statuses
If you don’t set explicit token permissions, it will have write access to most of the resources mentioned above.
Why Do I Need to Modify GITHUB_TOKEN Permission?
As a security best practice, it’s essential to set minimum token permissions for GitHub Actions workflows. This will ensure that even if GITHUB_TOKEN is compromised, it will limit the extent of damage. There have been several real-world attacks related to GITHUB_TOKEN for malicious purposes. We have given a few examples below:
- In February 2024, security researchers demonstrated that they could steal GITHUB_TOKEN by a command injection vulnerability in the Bazel project.
- In January 2024, security researchers successfully carried out a supply chain attack on PyTorch and many other organizations, including GitHub itself, by exploiting CI/CD vulnerabilities in their repositories.
- Another similar incident took place in December 2020 when a security researcher broke into Microsoft’s Visual Studio Code GitHub repository. The attack was due to a vulnerability in the CI script, and the researcher was able to get write access to the repository.
Also read: Analysis of Backdoored XZ Utils Build Process with Harden-Runner
Check if a Workflow Has Elevated Permissions
If a GitHub Actions workflow file has the permissions property defined either at the workflow level or job level, it means that it has restricted permissions. The example below shows how one can explicitly set token permissions at the workflow and job level:
However, absence of the permissions property does not mean that the workflow has elevated permissions as one can also set default workflow permissions at the repository or organization level (more on this in the section below). The conclusive way to check GITHUB_TOKEN permissions is to look at the build log for a workflow run under the ‘Set up job’ step.
Set GITHUB_TOKEN Permissions
Read-only permissions by default
The best way to implement the least privileged permissions for GITHUB_TOKEN is to set “Read repository contents and package permissions” as workflow permissions in repository/organization settings. This will ensure that all workflows inside the repository/organization have read-only access by default.
Explicit permissions in workflow files
Alternatively, the default setting may grant all read and write permissions to GITHUB_TOKEN. In such cases, you will have to manually define the least privileged permissions for GITHUB_TOKEN within each workflow file. As this method requires manual updates to each file, it can be challenging to manage permissions across various workflows at one time.
You can explicitly set token permissions in workflow files. One advantage of setting explicit token permissions in workflow files is that it would secure all forks on the repositories as well.
To modify permissions of GITHUB_TOKEN, you must use the permission key in the concerned workflow file. If a workflow is using a permission key:
- GITHUB_TOKEN for the job will only have explicitly defined permissions in the permission key.
- GitHub will set all unspecified permissions to “no access”. The only exception here would be the metadata scope which is always given a “read access” permission.
- For forked repositories, permissions key can add or remove only “read” permissions. They can’t add “write” access for them.
Calculating Permissions for a Job
Before you start setting permissions, know what the initial default setting of the permissions is for your enterprise. If the default setting is restrictive, all the repositories in the enterprise will have the same permissions by default.
Now, to calculate the permission, check out the configurations of the workflow file, the workflow, and the job. You can modify the permissions of each of these levels separately.
To calculate minimum token permissions for a job, you need to analyze all the steps in it. For each step, you need to calculate minimum GitHub API permissions. For example, if a job step publishes a package on GitHub, it would require the packages:write permission. Similarly, if a job needs to manage pull requests, it will require the pull-requests:write permission. You can get minimum token permission for the job by adding minimum token permissions for all the steps in it.
If you were do this process manually, you may accidentally come up with restrictive permissions that break your workflow. You can iteratively come up with minimum token permissions by analyzing logs for failed workflow runs, identifying the GitHub APIs that return 403, and updating token permissions accordingly.
How StepSecurity Helps Enterprises Set Minimum GITHUB_TOKEN Permissions
To make it easy for you to understand the right GITHUB_TOKEN permission for each job and set minimum token permissions for each workflow, StepSecurity is here for you! Let’s explore how StepScurity helps organizations set minimum token permission for the workflow files at scale.
Knowledge base of Action permissions
StepSecurity has built a knowledge base of required permissions for popular GitHub Actions. Using this knowledge base, we can calculate minimum token permissions for most workflow files. Not only does StepSecurity calculate minimum token permissions, but our platform also creates automated pull requests to update workflow files. You can try this out by visiting https://app.stepsecurity.io/securerepo
Monitor GitHub API requests at runtime
For enterprises, StepSecurity’s CI/CD infrastructure security platform monitors outbound GitHub API calls to determine the necessary permissions for each job. By analyzing the HTTP method and path of these calls, StepSecurity calculates the minimum GITHUB_TOKEN permissions for each workflow job, ensuring a granular and secure access control model. This approach is helpful for private Actions as well as public Actions for which, the knowledge base does not exist currently. You can read more about this feature here.
Here are a few pull requests leveraging the StepSecurity platform to set minimum GITHUB_TOKEN permissions and more with automated pull requests:
- https://github.com/electron/electron/pull/36343
- https://github.com/prisma/prisma/pull/20617
- https://github.com/sequelize/sequelize/pull/15605
- https://github.com/bazelbuild/bazel/pull/18264
- https://github.com/ruby/ruby/pull/6811
- https://github.com/google/libphonenumber/pull/2913
- https://github.com/nodejs/undici/pull/2130
- https://github.com/iterative/dvc/pull/8496
- https://github.com/arkime/arkime/pull/2475
- https://github.com/apache/shiro/pull/706
- https://github.com/intel/pcm/pull/627
Eager to learn more about how StepSecurity Harden-Runner helps you determine minimum GitHub_Token permissions using eBPF? Deep dive into our blog post here: https://www.stepsecurity.io/blog/determine-minimum-github-token-permissions-using-ebpf-with-stepsecurity-harden-runner
Leverage StepSecurity to set the right GitHub permissions for all your workflows and get runtime security of GitHub API requests to enhance GitHub Actions security of your workflows. Try our app for free!
Conclusion
As we wrap up this blog about GITHUB_TOKEN, it is evident that setting minimum GITHUB_TOKEN permissions play a huge role in securing your GitHub Actions environment. Setting least privileged access for GITHUB_TOKEN is one of the most important recommended GitHub Actions security best practices and cannot be overlooked. With careful implementation of this best practice, you can gain control of your CI/CD environment and ensure its safety.
Frequently Asked Questions (FAQs)
How to generate a GitHub personal access token?
You don't need to manually generate a GitHub personal access token. GitHub automatically creates and injects the GITHUB_TOKEN for each job run in your GitHub Actions workflow. This token is specific to your repository and is used for authentication within GitHub Actions workflows.
How to use GITHUB_TOKEN?
To use the GITHUB_TOKEN in your GitHub Actions workflow, you can reference it as a secret called ${{ secrets.GITHUB_TOKEN }}. This GITHUB_TOKEN secret allows your workflow to authenticate and interact with GitHub's APIs on behalf of GitHub Actions, enabling various actions such as pushing changes, creating issues, and more.
Where do I find my GITHUB_TOKEN?
You can access the GITHUB_TOKEN within your GitHub Actions workflow using the ${{ secrets.GITHUB_TOKEN }} syntax. This token is automatically generated and injected by GitHub at the beginning of each job run, making it readily available for authentication and API interactions within your workflow.
How do I authenticate with GITHUB_TOKEN?
Authentication with the GITHUB_TOKEN is automatic within GitHub Actions workflows. When you reference ${{ secrets.GITHUB_TOKEN }} in your workflow file, GitHub automatically handles the authentication process for you. This token grants the necessary permissions to interact with GitHub APIs securely during the execution of your workflow.