TL;DR - GitGuardian harvested over 500,000 exposed RSA private keys from public sources and tested the GitHub App keys among them. Of 4,802 tested keys, 474 still worked, authenticating as 440 distinct Apps. Forty-four of those Apps had full organisation admin access, 72% could read private repository content, and 207 could write to it. One key that leaked in April 2025 gave write access to CDC repositories until 18 September 2026, which is seventeen months. GitHub App private keys do not expire. What you need to do: build the list of GitHub Apps with access to your repositories, check which of them hold write or admin permissions, rotate the private keys you cannot account for, and put secret scanning in front of your own repos so the next leak is your alert rather than somebody else's research paper.
By The Numbers
| Item | Value |
|---|---|
| RSA keys harvested | over 500,000 |
| GitHub App keys tested | 4,802 |
| Keys that still authenticated | 474 (about 10%) |
| Distinct Apps reached | 440 |
| Apps with org admin access | 44 |
| Apps able to read private repos | 72% |
| Apps able to write to repos | 207 |
| Longest known exposure | 17 months (CDC, April 2025 to 18 September 2026) |
I have a personal rule about GitHub Apps that I developed the boring way: whenever I inherit a repository in a new engagement, the first thing I look at is not the code. It is the list of Apps installed on the organisation, because that list is where permissions go to be forgotten.
So when a report lands that says researchers found 474 working private keys for 440 different GitHub Apps, my reaction is not surprise at the number. It is recognition. The number is what you get when an ecosystem has a credential type that never expires, a permission model most people set once during onboarding, and no natural moment where anybody reviews the list.
Let me walk you through what GitGuardian actually did, why GitHub App keys behave worse than the secrets you are already careful about, and the specific inventory you can build in an afternoon.
What the researchers actually found
GitGuardian collected more than 500,000 exposed RSA private keys from public sources and tested the ones belonging to GitHub Apps against GitHub's API. Of 4,802 keys tested, 474 still authenticated, roughly ten percent. Those 474 keys reached 440 distinct Apps, which is the number that matters more than the key count: one working key is one App's worth of access, and 474 keys mapped onto 440 Apps tells you the leaks are spread across a wide estate rather than concentrated in a few careless projects.
The permission profile of those Apps is where the report gets uncomfortable:
- 44 Apps held full organisation admin access
- 72% could read private repository content
- 207 could write to repositories
A GitHub App with organisation admin and write access is not a read-only integration that leaks some metadata. It is an identity that can change code, and in the admin cases, change who else can reach the organisation.
The concrete example is the one to remember, because it maps the abstract risk to a timeline: a CDC private key leaked in April 2025 and retained write access to CDC repositories until 18 September 2026. Seventeen months. Nobody in that window was doing anything outrageous; the key simply did not expire, nobody knew it was public, and the platform had no reason to complain because the key was valid.
Named in the report alongside the CDC: BuildBuddy, Crusher.dev, and access tokens for GitHub Actions. That last one is worth noting for a practical reason. If you think of App keys as something a platform team manages in a settings page, remember that the same class of credential shows up in workflow configurations and CI jobs, which is exactly where a hurried commit puts it.
Why App keys are worse than the secrets you already guard
Most credential hygiene advice assumes a secret that either expires, or fails loudly when it leaks. App private keys are the opposite case on both counts.
They do not expire. A GitHub App authenticates by signing a short-lived JWT with its private key, then exchanging that for an installation access token that lives about an hour. The short-lived part is the token. The private key underneath it is durable, and nothing about the design forces a rotation. Rotation is a manual act that a human has to decide to perform.
They fail silently in the direction that hurts. If a personal access token with a short expiry leaks, the blast radius is bounded by the clock. If an App private key leaks, the blast radius is bounded by how long it takes somebody to notice, which in the CDC case was seventeen months and in the 474-key population may still be ongoing.
Their permissions are set once and rarely revisited. App permissions are configured at installation, with a scope of repositories chosen at that moment. A 2023 integration installed to scan three repositories keeps whatever access it was granted, whether or not the project still exists.
They authenticate as a machine, so nothing looks unusual. There is no login prompt, no MFA challenge, no impossible-travel alert that fires when a key in a public repository is used from somewhere unexpected. The App is supposed to be making automated calls. That is what makes the abuse quiet.
You cannot rotate what you have never inventoried. Everything else in this post is downstream of that sentence.
The inventory you can build this afternoon
This is the part I would actually schedule, and it is smaller than it sounds if you scope it to the organisation level rather than trying to be forensic about every repository.
1. List every installed GitHub App on the organisation.
In GitHub's organisation settings, the installed Apps list shows each App, its permissions, and which repositories it can reach. Record four things for each one: who installed it, when, what permissions it holds, and whether anyone currently recognises the name. The unrecognised entries are your priority list, and in most organisations there are at least a few, because people install integrations during proofs of concept and leave them running.
2. Flag anything with admin, write, or broad organisation scope.
Sort your list by permission rather than by age. An App with organisation admin and write access to all repositories is the one that converts a leaked key into an incident. An App that reads metadata from two repositories is a much smaller problem. This sort is what lets you have a proportionate conversation instead of a panicked one.
3. Check which App keys live anywhere public.
GitHub's secret scanning covers partner patterns and can be enabled for your organisation, with push protection to stop new secrets reaching the remote in the first place. Enable it if you have not: the 474 keys in this report were found in public sources, which means the organisations behind them had no control stopping the exposure. Your own repositories are the easiest place to start and the only place you can fully control.
4. Rotate the keys you cannot account for, and delete the old ones.
Generating a new private key for an App takes a couple of clicks, and the old key remains valid until it is explicitly removed. Do both steps. The most common failure I see in a rotation exercise is a team that generates replacements and never revokes the originals, which doubles the number of valid keys rather than reducing it.
5. Reduce the access the App asks for, now that you are looking at it.
Least privilege for an App is a permissions set and a repository scope. Narrowing an over-permissioned App takes minutes, and it is the only change on this list that reduces the impact of the next leak rather than today's.
The CI/CD angle
There is a version of this problem that is entirely inside your own walls, and it is worth a separate look: the same key material travels through pipelines. A workflow that needs to authenticate as an App to push a release will have that key available to the job, and keys have a habit of ending up in job logs, build artefacts, cache directories and developer machines.
The specific things I check in a repository review are the ones that most often expose a credential: workflow files that echo environment variables, build scripts that write configuration to disk in a workspace that gets cached, and deployment pipelines that pass secrets through command-line arguments, where they land in process listings.
None of that requires the key to be in a public repository to be a problem. A key in a private repository that thirty contractors can read is a key that many people hold.
What this says about the ecosystem
GitHub Apps are a genuinely good design. Machine identities with scoped permissions are better than shared service accounts with a personal access token, which is what they replaced in a lot of organisations. The permission model is finer-grained, the tokens are short-lived, and the audit story is better than it was.
What the design does not do is force a lifecycle. There is no expiring credential, no periodic attestation that the App is still needed, and no default prompt to review installations. That is not a GitHub flaw so much as a gap that exists in every platform where a durable credential secures an automated integration: the platform provides the capability, and the lifecycle is the customer's job.
The good news is that this gap is closable with process rather than tooling. A quarterly review of installed Apps, with the four facts recorded per App, is a small recurring task that would have caught most of the 474. The bad news is that it is the kind of task that only gets done after something like this report, which is why reports like this one exist.
Key Takeaways
- 474 of 4,802 tested App keys still authenticated, reaching 440 distinct Apps. Roughly ten percent of the exposed keys were live.
- 44 Apps had full organisation admin access, 72% could read private repositories, and 207 could write to them.
- GitHub App private keys do not expire. Only the installation tokens they mint do. Rotation is a manual decision nobody is prompted to make.
- The CDC key leaked in April 2025 and kept write access until 18 September 2026. Seventeen months of valid access to a public key is the failure mode in one line.
- List your installed Apps with permissions, installer and scope. Sort by permission rather than age, and treat unrecognised Apps as the priority.
- Rotation means generating a new key and deleting the old one. Replacing without revoking doubles your valid key count.
- Enable secret scanning and push protection on your own repositories. The 474 keys came from public sources, which is exposure you can prevent on your own code.
FAQ
What is a GitHub App private key?
The credential a GitHub App uses to prove its identity. The App signs a JSON Web Token with the private key and exchanges it for an installation access token that expires in about an hour. The private key itself is durable and does not expire on its own.
How many of the leaked keys still worked?
474 out of 4,802 keys tested, about ten percent. They authenticated as 440 distinct Apps.
Why is that worse than a leaked personal access token?
A personal access token has an expiry that bounds the damage. An App private key has no expiry, so the exposure lasts until somebody notices and revokes it. The CDC key retained write access for seventeen months.
How do I find which GitHub Apps have access to my organisation?
In your organisation settings there is an installed Apps list showing each App, its permissions and the repositories it can reach. Record the permissions, the installer and the scope, then compare that against who currently recognises the App.
Should we rotate every App key now?
Rotate the keys you cannot account for, and prioritise Apps holding admin or write permissions. Rotating everything in one weekend without a working inventory tends to break integrations, which trains people to avoid the next rotation.
Does secret scanning stop this?
It catches secrets that are pushed to repositories where scanning is enabled, and push protection can block them from arriving in the first place. Enable both for your organisation. It does not help with keys that leaked somewhere you do not control, which is why the inventory comes first.
We are a small team. How much of this applies to us?
The inventory applies at any size, and it is faster for small teams because the list is shorter. The failure pattern this report describes is not a large-enterprise problem, it is a nobody-owns-the-list problem.
What is the single most useful thing to do this week?
Produce the list of installed Apps with their permissions and scopes. It is an afternoon's work, it is the prerequisite for every other action here, and you cannot rotate what you have never inventoried.
My Take
The part of this report I keep thinking about is not the 474 keys. It is that the number is the same order of magnitude as the number of Apps, which tells you the leak rate is a property of the ecosystem rather than of a few careless organisations. When ten percent of exposed App keys still work, the interesting question stops being "who was careless" and becomes "why does a working key stay working for seventeen months".
I think the honest answer is that we have all been trained to think about credential hygiene as a rotation schedule, and App keys are a credential class where no schedule exists unless you invent one. Every other secret in the average shop either expires on its own or sits behind something that does. This one sits there, valid, waiting for somebody to notice it is public.
So the useful response is not to be frightened of GitHub Apps. It is to write down the list, which is a genuinely small piece of work that almost nobody has done, and then decide a cadence for keeping it. The organisations that come out of this report without a problem will be the ones that can answer one question quickly: what Apps can write to our repositories, and who owns each of them.
Mathew Clark Founder, SecureInSeconds Currently: reviewing an App installation list where nobody recognises three of the names
Further Reading
- GitHub App private keys: 474 leaked keys still work (GitGuardian)
- GitHub docs: managing private keys for GitHub Apps
- GitHub docs: reviewing installed GitHub Apps in your organisation
- GitHub docs: secret scanning and push protection
- Hundreds of leaked GitHub App keys still authenticate (Infosecurity Magazine)
- F5 BIG-IP: a 9.8 you reach through the auth header, with no credentials
- Android 17 turned on certificate transparency: what breaks and what is exempt



