Security advisory: Possible leak of legacy API keys via improper cache configuration - RubyGems Blog
Skip to content
Security advisory: Possible leak of legacy API keys via improper cache configuration
Colby Swandale
·
22 Jul 2026
Technical lead at RubyCentral.
A CDN caching bug on RubyGems.org could hand one account’s API key to another person for up to an hour. If you signed in to RubyGems.org with a gem client older than v3.2.0, your key could have been exposed (the technical details are below). Currently, 18% of sign-ins through gem signin come from an affected version, and for the first several years of this bug, before we changed the client’s sign-in path in December 2020, it was every gem client.
We’ve reviewed the access logs we keep and found no sign of a legacy key being used maliciously, and no user or support report has suggested otherwise. Those logs only cover a recent window, though, a small slice of the years this bug is estimated to have been present. Rather than lean on log analysis to rule out any potential abuse, we’ve revoked every RubyGems.org legacy key, and we’re asking owners to check their own gems.
Your already-published gems were never at risk: existing releases cannot be rewritten, and installing gems was never affected. What was at risk is the key itself. Anyone holding a leaked key could act as that account: publish a new gem version, yank versions, add themselves as an owner.
If you had MFA enabled for API requests, your key could still have leaked, but MFA blocks a leaked key from pushing, yanking, or changing owners.
What account holders should check
Existing releases cannot be rewritten, so if you’re an account owner, what you are checking for is anything added or removed under your name. On each of your gems, look for versions you did not publish (especially one higher than your latest), unexpected yanks, unfamiliar owners or maintainers, trusted publishers you did not configure, and webhooks you do not recognise.
We’re notifying users whose account held a legacy API key at any point, since those are the accounts this bug could have exposed, so please keep an eye on your inbox. You can immediately review your account’s API Key history through your profile page.
What is a RubyGems.org Legacy API Key
A Legacy API key is a single credential that carries every gem-management permission at once. It isn’t tied to a single gem, so those permissions apply across every gem you own, and it has no expiry, so it stays valid until you delete it.
In practice that means the key can do many operations the API exposes for publishing and maintaining your gems: push a new version, yank an existing one, add or remove owners, change webhooks, and configure trusted publishers. What it can’t do is act as a full account login. No API key can change your password or email, alter your MFA settings, or delete your account.
The word “legacy” undersells how ordinary these keys were. When this endpoint was built, and for years afterward, this was simply the API key: the only kind RubyGems.org issued, and the only way the gem CLI could sign you in. Scoped keys arrived in RubyGems v3.2.0 in December 2020, and it’s at that point the older keys became “legacy”. So for most of the roughly nine years this bug existed, the affected credential wasn’t a deprecated relic that a few stragglers still used. It was the standard way everyone authenticated.
Who is affected
Users of the rubygems client older than v3.2.0, which signs in by calling GET /api/v1/api_key. Notably, this includes the vendored rubygems with current macOS (Tahoe), version 3.0.3.1 at /usr/bin/gem, so the affected client is a standard system tool, not an edge case.
Recipients of a leaked key, who may be entirely uninvolved account holders. A cached hit wrote another account’s key into their local credentials file, and from that point they acted as the other account until they signed in again.
Technical detail
Under a specific interaction between the response compression and cache headers, our CDN cached the successful response and served the same freshly created key to subsequent callers on the same edge node (POPs) for up to an hour, without re-checking their credentials.
The result was that one account’s API key could be handed to another party, with no attacker involved. Once one user’s sign-in response was cached at a Fastly edge node, the next user signing in through that same node within the hour received the earlier user’s key instead of a fresh one of their own. And because cache hits were served at the edge without contacting the origin, an unauthenticated party could also poll the endpoint to harvest whatever key happened to be cached.
The chain is as follows:
GET /api/v1/api_key authenticates with HTTP Basic and creates a new legacy key, returning it in a 200 body.
The Ruby client sends Accept-Encoding: gzip by default (via Net::HTTP). Rack::Deflater replaces the response body with a...