Incident Response

GitHub OAuth Token Theft: The Heroku and Travis CI Breach

Attackers stole OAuth tokens from Heroku and Travis CI to access private GitHub repositories across dozens of organizations, including npm itself. The full scope of the breach took weeks to unravel.

Alex
DevSecOps Engineer
5 min read

On April 15, 2022, GitHub publicly disclosed that stolen OAuth user tokens, originally issued to Heroku and Travis CI, had been used to access private repositories belonging to dozens of organizations. The breach was a sobering demonstration of how third-party integrations create attack paths that organizations often fail to account for in their threat models.

The Full Timeline

The attack unfolded over weeks before detection:

  • April 9, 2022: GitHub Security identifies anomalous activity involving GitHub API access using OAuth tokens from Heroku and Travis CI
  • April 12, 2022: GitHub discovers unauthorized access to npm's production infrastructure using AWS keys found in downloaded private repositories
  • April 13-14, 2022: GitHub notifies Heroku (Salesforce) and Travis CI. Begins systematic analysis of affected OAuth tokens
  • April 15, 2022: GitHub publicly discloses the incident. Heroku begins revoking all GitHub integration OAuth tokens
  • April 16-18, 2022: GitHub completes token revocation for Heroku Dashboard and Heroku Review Apps OAuth applications
  • April 22, 2022: Travis CI revokes all OAuth tokens for its GitHub integration
  • May 26, 2022: Heroku discloses that the initial compromise involved a machine account's OAuth token accessing a Heroku database containing customer tokens

The gap between initial compromise and detection — estimated at potentially weeks — highlights the challenge of detecting token abuse when the tokens are used in ways that look superficially legitimate.

Attack Mechanics

The attack followed a pattern that is becoming disturbingly common:

Step 1: Initial Access

Attackers gained access to Heroku's internal infrastructure, likely through a compromised machine account. From there, they accessed a database containing OAuth tokens that Heroku's GitHub integration stored on behalf of its users.

Step 2: Token Harvesting

With database access, the attackers extracted OAuth tokens for users who had connected their GitHub accounts to Heroku. These tokens had the scopes originally granted by each user — in many cases, repo scope, which provides full read/write access to all of the user's repositories, including private ones.

Step 3: Repository Enumeration and Download

Using the stolen tokens, attackers authenticated to GitHub's API as the legitimate users and:

  • Listed all repositories accessible to each user
  • Identified high-value targets (private repositories with sensitive content)
  • Downloaded repository contents using the Git API

Step 4: Lateral Movement

In repository contents, the attackers found additional credentials — AWS access keys, API tokens, database passwords — that enabled access to additional systems. The most significant lateral movement was into npm's AWS infrastructure.

The npm Dimension

The most alarming aspect of this breach was the compromise of npm's infrastructure. Attackers used AWS keys found in npm's private GitHub repositories to access npm's S3 storage.

GitHub's investigation determined:

  • Package metadata was accessed (including package manifests that could reveal dependency relationships)
  • No packages were modified (the attackers had read access but apparently did not leverage write access)
  • npm user credentials were not exposed (password hashes and 2FA tokens were stored separately)

But the hypothetical scenario — attackers modifying popular npm packages to include malicious code — would have constituted one of the largest software supply chain attacks in history. The npm registry serves billions of downloads per week. A compromised package like lodash or express could have affected millions of applications.

Why This Breach Was Different

Most supply chain attacks target the software itself — injecting malware into packages, compromising build systems, or exploiting vulnerabilities. This attack targeted the development infrastructure surrounding software.

The attacker did not need to:

  • Find a vulnerability in any application
  • Compromise any maintainer's credentials directly
  • Bypass any package signing or verification

They simply needed to compromise one third-party integration that had broad access to code repositories. This is a fundamentally different threat model than most organizations plan for.

Organizational Blind Spots

Forgotten Integrations

How many GitHub OAuth integrations does your organization have? Most security teams cannot answer this question confidently. Integrations are typically set up by individual developers or teams, often without security review.

Overprivileged Tokens

When Heroku asks for repo scope, it gets access to every private repository the user can see — not just the ones relevant to Heroku. Most OAuth integrations request more access than they strictly need, and users grant it without scrutiny.

No Token Lifecycle Management

OAuth tokens persist indefinitely unless explicitly revoked. An integration authorized three years ago by a developer who has since left the company still has a valid token. Most organizations never audit or rotate these tokens.

Shared Risk, Unshared Visibility

Your organization's GitHub security posture is only as strong as the weakest third-party integration connected to it. But you have no visibility into Heroku's internal security practices. You cannot audit their token storage, monitor their incident response, or assess their risk.

Concrete Remediation Steps

Immediate Actions

  1. Audit all GitHub OAuth integrations at github.com/settings/applications for personal accounts and organization settings for org-level integrations
  2. Revoke unnecessary integrations — if you have not used an integration in 6 months, revoke it
  3. Rotate all secrets stored in GitHub repositories — even private ones
  4. Enable GitHub audit log streaming to detect anomalous API access patterns

Structural Changes

  1. Use GitHub Apps instead of OAuth where possible — GitHub Apps have granular, repository-specific permissions rather than user-wide access
  2. Implement SAML/SSO for your GitHub organization to centralize access control
  3. Deploy IP allowlists for API access to limit where tokens can be used from
  4. Never store secrets in repositories — use proper secrets management (Vault, AWS Secrets Manager, etc.)

How Safeguard.sh Helps

Safeguard.sh monitors your development infrastructure for credential exposure and integration risks. Our platform inventories all third-party integrations connected to your code repositories, tracks their permission scopes, and alerts on anomalous access patterns. When a breach like the Heroku/Travis CI incident is disclosed, Safeguard.sh helps you immediately assess your exposure by identifying which repositories were accessible through compromised integrations and whether any secrets in those repositories need rotation.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.