Back to Blog
Resources

GITHUB_TOKEN: How It Works and How to Secure Automatic GitHub Action Tokens

Explore the ins and outs of GitHub token- from using it securely, risks involved, and setting the right token permissions to keep your workflows secure.
Ashish Kurmi

November 8, 2024

Table of Contents

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.

StepSecurity app control for minimum token permissions
StepSecurity app control for minimum token 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

Try Our App for Free

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:

name: Build and Publish
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@49d9c2e46838527972659f80f5488d08971fdc2d
with:
name: docker.pkg.github.com/elgohr/publish-docker-github-action/publish-docker-github-action
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: docker.pkg.github.com

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.

name: build

on:
push:
tags:
- "v**"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
with:
ref: ${{ github.event.release.tag_name }}
- run: npm ci
- run: npm run build
- uses: JasonEtco/build-and-tag-action@dd5e4991048c325f6d85b4155e586fc211c644da # v2.0.1
env:
GITHUB_TOKEN: ${{ github.token }}

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.

Feature 

GITHUB_TOKEN 

Personal Access Token (PAT) 

Scope 

Automatically generated for each job in a workflow. 

Manually generated by users and can be scoped as broadly or narrowly as needed. 

Permissions 

Permissions are limited to the repository that contains the workflow. 

Permissions can be configured during creation, potentially granting full control over the user's repositories and other features. 

Lifespan 

Expires when the job finishes. 

Does not automatically expire but can be set to expire up to a maximum of one year. Users can also manually revoke it at any time. 

Generation 

Automatically generated by GitHub Actions. 

Must be manually generated by the user through GitHub's settings. 

Use Case 

Ideal for automated tasks within a single repository, like CI/CD pipelines. 

Suitable for tasks that require access to multiple repositories or extended GitHub functionality beyond the current repository. 

Revocation and Rotation 

Automatically handled by GitHub. 

The user is responsible for manually revoking or rotating tokens. 

Exposed to Workflow Configurations 

No manual exposure required; automatically available in workflows. 

Must be manually added to a repository's secrets to be used in workflows. 

Accessible in Pull Requests from Forks 

Limited access by default for security reasons. 

Typically not recommended for use in workflows triggered by pull requests from forks due to security concerns. 

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:

name: Release
on:
push:
branches:
- int
permissions:
contents: read
jobs:
release:
permissions:
contents: read
runs-on: ubuntu-latest

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.  

Screenshot of a build log for a demo workflow run
The build log for a demo workflow run under ‘Set up job’

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.  

Screenshot of a repository setting
Repository setting where you can select "Read repository contents and package permissions"

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

Screenshot of the StepSecurity app calculating minimum token permissions
The StepSecurity App calculates minimum token permissions for the demo repo

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.

Screenshot of StepSecurity recommending GITHUB_TOKEN permission for a workflow
StepSecurity recommending GITHUB_TOKEN permissions

Here are a few pull requests leveraging the StepSecurity platform to set minimum GITHUB_TOKEN permissions and more with automated pull requests:

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!

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.

Blog

Explore Related Posts