DEV Community

Cover image for Pinning GitHub Actions with Commit SHA for Supply Chain Defense
Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Pinning GitHub Actions with Commit SHA for Supply Chain Defense

Pinning actions in GitHub Actions workflows with a commit SHA value is a fundamental security practice that strengthens defense mechanisms against software supply chain attacks. This method precisely points to a specific version of an action, protecting it from unexpected or malicious changes. In this guide, we will delve into SHA pinning strategies in GitHub Actions and how you can improve your security posture in CI/CD processes.

In many projects, GitHub Actions workflows use tags or branches like uses: actions/checkout@v4 or uses: docker/build-push-action@v5. While this approach offers convenience, it means the underlying code can change, and these changes might unknowingly introduce risks into your workflow. Pinning with a commit SHA eliminates precisely this uncertainty.

Software Supply Chain Attacks and GitHub Actions Risks

Software supply chain attacks aim to interfere with the components or processes used in the development, distribution, or production of software, rather than directly attacking target systems. Such attacks can cause widespread damage by injecting malicious code into the software before it reaches the end-user. CI/CD platforms like GitHub Actions are critical links in this chain and present attractive targets for attackers.

A developer of a third-party action used in a GitHub Actions workflow might intentionally or unintentionally (e.g., through their repository being compromised) inject malicious code. If your workflow references this action via a branch or tag, malicious changes could be incorporated into your workflow instantly and without your approval. This situation can lead to serious security breaches, such as access to sensitive data, credential leaks, or direct deployment of harmful code to production environments.

⚠️ Critical Risk Areas

In a GitHub Actions workflow, especially when using external actions, there are multiple points of risk:

  • **Action Owner Attack:** The action's developer might publish a malicious update.
  • **Action Repository Compromise:** The action's code repository might be compromised, and malicious changes injected.
  • **Dependency Injection:** Vulnerabilities might leak through dependencies used by the action.
  • **Accidental Malicious Updates:** The action developer might unknowingly publish code containing a security vulnerability.

These risks directly affect the reliability of automated CI/CD processes and can jeopardize the integrity of your product.

What is Commit SHA Pinning and Why is it Important?

Commit SHA pinning refers to referencing an action used in your GitHub Actions workflow with a cryptographic hash value (typically a 40-character SHA-1 commit hash) that represents a specific snapshot of that action's source code. Using uses: actions/checkout@a5ac7e5831b6899ce3f18c2a40279220d8fe3078 instead of uses: actions/checkout@v4 guarantees that the action will always run the same, immutable code.

This method brings the principle of "immutability" to CI/CD processes. A commit SHA value is immutable; once created, its content cannot be changed. This ensures that when you pin an action with an SHA, future updates or malicious changes to that action will not affect your workflow unless you manually update the new SHA value. This significantly reduces security risks in the software supply chain.

Security Advantages

The main security advantages of SHA pinning are:

  1. Full Control and Transparency: You know exactly which code is running in your workflow. Any change requires manual approval and update.
  2. Immutability: A pinned SHA ensures that the action's code is not affected by future changes. This prevents malicious updates or accidentally introduced vulnerabilities from automatically leaking into your workflow.
  3. Auditability: In the event of any security incident, you can precisely determine which action version was run. This is vital for root cause analysis.
  4. Preventing Version Conflicts: Tags can sometimes be re-pointed or overwritten. An SHA, however, is always unique and fixed, eliminating such version conflicts.

Diagram

This diagram summarizes how supply chain risks emerge and how SHA pinning, along with other security measures, forms a comprehensive defense mechanism. SHA pinning is the first and most direct line of defense in this chain.

How to Implement Commit SHA in GitHub Actions?

Implementing commit SHA pinning in your GitHub Actions workflows is quite straightforward, but finding and managing the correct SHA value requires some attention. For each action, you need to find the latest commit SHA value of the version you are using and add it to your workflow. These steps must be repeated for each action individually.

Finding the Latest Commit SHA Value

There are several ways to find the latest commit SHA value of a GitHub Action:

  1. Via GitHub Interface:

    • Go to the GitHub repository of the action you want to use (e.g., actions/checkout).
    • On the repository's main page, under the "Code" tab, you will see the latest commit. Click on the first few characters of the SHA value next to the commit message.
    • On the commit details page that opens, you can find the full SHA value next to the "commit" label. Copy this value.
    • Alternatively, if you are using a tagged version (e.g., v4), you need to click on that tag and find the SHA of the corresponding commit. This information is usually found in the release notes at https://github.com/actions/checkout/releases/tag/v4 or in the commit itself.
  2. Using the git ls-remote Command:

    • This method allows you to programmatically retrieve the latest commit SHA value for a specific branch or tag.
    • For example, to get the SHA of the v4 tag for the actions/checkout action, you can use the following command:

      git ls-remote https://github.com/actions/checkout v4
      

      The output of this command will be in the format a5ac7e5831b6899ce3f18c2a40279220d8fe3078 refs/tags/v4 (or similar). The first part is the SHA value you are looking for.

Implementation in the Workflow File

Once you have found the SHA value, you simply need to update the uses line of the relevant action in your GitHub Actions workflow (YAML) file.

Before (Using Tag):

name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/build-push-action@v5
        with:
          context: .
          push: false
          tags: my-app:latest
Enter fullscreen mode Exit fullscreen mode

After (Pinned with Commit SHA):

name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@a5ac7e5831b6899ce3f18c2a40279220d8fe3078 # Current SHA for actions/checkout@v4
      - uses: docker/build-push-action@0a2c74878d6502d78705507b775d742023939883 # Current SHA for docker/build-push-action@v5
        with:
          context: .
          push: false
          tags: my-app:latest
Enter fullscreen mode Exit fullscreen mode

In this example, actions/checkout@v4 and docker/build-push-action@v5 actions have been replaced with the specific commit SHA values of their latest stable versions. This ensures that any future updates to these actions will not affect your workflow unless you manually change the SHA value.

💡 Automation Tips

In large projects or workflows using many actions, manually performing SHA pinning can be tedious. To automate this process, you might consider the following approaches:

  • **Dependabot:** GitHub's own dependency management tool, Dependabot, can track updates for GitHub Actions and create pull requests containing SHA values.
  • **Custom Scripts:** In your own CI/CD processes or local development environment, you can write Python or Bash scripts that scan your workflow files and pull the latest SHA values using commands like `git ls-remote` to update them.
  • **GitHub API:** You can query the SHA values of tag or branch references in repositories using the GitHub API and programmatically update workflow files.

These automations help reduce management overhead while maintaining your security posture.

Advantages and Disadvantages of SHA Pinning (Trade-offs)

While pinning with commit SHA in GitHub Actions offers strong security benefits, it also introduces some management challenges. As with any technical decision, it's important to carefully weigh the advantages and disadvantages and strike a balance based on your project's needs.

Advantages

  1. Increased Security: The most apparent advantage is significantly better defense against software supply chain attacks. It prevents malicious or vulnerable updates from automatically leaking into your workflow.
  2. Reproducibility: Since your workflows will always run the same code, the reproducibility of build and test results increases. This supports debugging and a consistent development process.
  3. Auditability: Because it's precisely recorded which version of an action ran, security audits and compliance checks become easier.
  4. Reliability: Reduces the risk of your workflows breaking due to unexpected changes or backward incompatibilities in third-party actions.

Disadvantages

  1. High Maintenance Overhead: When new versions of actions are released, you need to update the SHA values in your workflow files manually or with automated tools. This process can create significant management overhead in large projects using many actions and repositories.
  2. Risk of Missing Security Patches: If you don't regularly update SHA values, you might miss critical security patches or bug fixes in the actions you use. While this protects you from new attacks, it can also leave you vulnerable to known exploits.
  3. Developer Experience: The process of finding and adding the SHA value when adding a new action or updating an existing one adds an extra step for developers and can slow down development speed.

ℹ️ Striking a Balance

Despite the additional management overhead introduced by SHA pinning, its security benefits often outweigh this burden. Especially in projects dealing with sensitive data or having high security requirements, SHA pinning is an indispensable practice. Utilizing automation tools like Dependabot and establishing regular update processes are critical to reducing maintenance overhead. Our goal is to achieve the best security posture without excessively slowing down development speed.

Automation and Management Strategies

To alleviate the maintenance burden of SHA pinning and make this security practice sustainable, it is essential to develop automation and effective management strategies. Manually tracking and updating hundreds of SHA values is prone to human error and time-consuming.

Automated Updates with Dependabot

GitHub's built-in dependency management tool, Dependabot, can also be configured for GitHub Actions. Dependabot detects new versions of actions in your workflow files and creates pull requests (PRs) for these updates, including the commit SHA values.

Dependabot Configuration Example (.github/dependabot.yml):

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly" # Check for updates weekly
    # Commit-based update strategy for SHA pinning
    target-branch: "main"
    labels:
      - "dependencies"
      - "github-actions"
    commit-message:
      prefix: "fix"
      include: "scope"
Enter fullscreen mode Exit fullscreen mode

With this configuration, Dependabot will check all GitHub Actions usages in the main branch weekly and automatically open PRs for new commit SHA values. You can keep your actions up-to-date by regularly reviewing and merging these PRs.

Custom Scripts and CI/CD Integration

If you have more specific needs or if Dependabot's capabilities are insufficient, you can automate this process by writing your own scripts. For example, you can write a Python script that parses your workflow files, queries the GitHub repository of each action, pulls the latest SHA value, and updates the file.

You can run this script, for example, with a weekly Cron job or as a step in your existing CI/CD pipeline. You can also automate the approval process by having it automatically commit the updated workflow files and open a PR.

# Draft of an example Python script (not fully functional)

    if "object" in response.json() and response.json()["object"]["type"] == "commit":
        return response.json()["object"]["sha"]
    # If it's a direct commit SHA, get it from the 'sha' field
    return response.json()["object"]["sha"] # or response.json()["sha"] if it points directly to a commit

def update_workflow_file(file_path):
    """Updates action references in the workflow file with SHAs."""
    with open(file_path, 'r') as f:
        content = f.read()

    updated_content = content
    # Find patterns like uses: actions/checkout@v4
    pattern = re.compile(r"uses:\s*([\w-]+/[\w-]+)@([\w\d.-]+)")

    for match in pattern.finditer(content):
        full_action = match.group(0)
        repo_path = match.group(1) # actions/checkout
        ref = match.group(2)       # v4 or main

        owner, name = repo_path.split('/')

        try:
            latest_sha = get_latest_sha(owner, name, ref)
            print(f"Updating {full_action} to SHA: {latest_sha}")
            updated_content = updated_content.replace(full_action, f"uses: {repo_path}@{latest_sha}")
        except requests.exceptions.RequestException as e:
            print(f"Error fetching SHA for {repo_path}@{ref}: {e}")
            continue

    with open(file_path, 'w') as f:
        f.write(updated_content)

if __name__ == "__main__":
    # Example usage: Add the actual file path here
    # update_workflow_file(".github/workflows/ci.yml")
    print("Workflow update script ran.")
Enter fullscreen mode Exit fullscreen mode

Such a script can dynamically fetch the latest SHA values and update workflow files. Of course, this script would need to authenticate with a GitHub API token and handle issues like rate limiting.

Periodic Review and Approval Processes

No matter how powerful automation is, human oversight is always important. At regular intervals (e.g., monthly or quarterly), you should establish a security review process that verifies all actions used in your workflows are pinned with current SHA values and that these actions are still secure.

During this process, it is beneficial to review the release notes of updated actions to check for any security fixes, new features, or potential backward incompatibilities. This way, you both ensure security and maintain the performance and stability of your software.

The Place of SHA Pinning in Comprehensive Supply Chain Defense

Pinning with commit SHA is an important part of software supply chain security, but it is not sufficient on its own. A comprehensive defense strategy combines multiple layers of security to protect your CI/CD pipeline and your final product against various attack vectors.

While SHA pinning addresses the principle of "ensuring the code you trust hasn't changed," other security measures target different risk areas:

  1. Secure Secret Access with OIDC (OpenID Connect): Instead of directly accessing sensitive information (API keys, cloud credentials) via secrets in GitHub Actions, using OIDC to obtain temporary and short-lived credentials minimizes the risk of secret leakage. This is an application of the "least privilege" principle.
  2. CODEOWNERS and Branch Protection Rules: Define CODEOWNERS for critical workflow files and source code repositories, and implement branch protection rules to ensure changes are approved only by authorized individuals. This prevents malicious or accidental changes.
  3. Static Application Security Testing (SAST) and Dynamic Application Security Testing (DAST): Integrate SAST tools (e.g., SonarQube, Bandit) into your CI/CD process to detect code vulnerabilities early. DAST tools (e.g., OWASP ZAP) are used to find security vulnerabilities in the running application.
  4. Dependency Scanning: Scan all third-party libraries and dependencies (npm packages, pip packages, Docker images) used by your project for known security vulnerabilities (e.g., Trivy, Snyk, Dependabot). This reduces indirect supply chain risks.
  5. Artifact Signing and Verification: Use cryptographic signing to verify the integrity and origin of compiled products (artifacts). Verifying these signatures during the deployment phase protects against man-in-the-middle attacks.
  6. Access Control and Segmentation: Implement strict access controls between different components and services in your CI/CD environments. Isolate sensitive systems with network segmentation to reduce the risk of lateral movement.
  7. Zero Trust Architecture: Adopt a "never trust, always verify" approach that treats every access request, even from within the internal network, as a potential threat. This can be extended, especially with ZTNA (Zero Trust Network Access) solutions.

This layered security approach prevents a single vulnerability from compromising the entire system. SHA pinning is a fundamental building block of these layers, providing you with control over "ensuring an action behaves as I expect."

Conclusion

Pinning GitHub Actions with a commit SHA is a vital step to enhance software supply chain security in modern software development processes. This practice guarantees the integrity and immutability of third-party actions used in your CI/CD workflows, significantly reducing the risk of malicious or vulnerable code injection. Although this method introduces additional management overhead, the security benefits it provides make the effort worthwhile.

Depending on the size of your project and its security requirements, you can simplify the management of SHA pinning by leveraging automation tools like Dependabot or developing custom scripts. Remember that SHA pinning is only one component of a comprehensive supply chain security strategy. When combined with other security layers such as OIDC for secret management, dependency scanning, code analysis, and zero-trust principles, the security of your CI/CD environment and final product can be maximized. Implementing these steps not only strengthens your security posture but also increases the reliability and reproducibility of your software.

Official Resources

Top comments (0)