Skip to content

Fix API Key Limit Race Condition Bypass#5620

Merged
DaneEveritt merged 1 commit into
pterodactyl:1.0-developfrom
rossnoah:1.0-develop
May 23, 2026
Merged

Fix API Key Limit Race Condition Bypass#5620
DaneEveritt merged 1 commit into
pterodactyl:1.0-developfrom
rossnoah:1.0-develop

Conversation

@rossnoah
Copy link
Copy Markdown
Contributor

@rossnoah rossnoah commented Apr 9, 2026

The API key creation endpoint checks that a user has fewer than 25 keys before creating a new one. The problem is that the count was read from an eager-loaded collection ($user->apiKeys->count()) with no lock held, so concurrent requests could both pass the check and each create a key, pushing the user past the 25-key cap.

The fix wraps the count check and key creation in a single database transaction with lockForUpdate() on the query. Only one request at a time can evaluate and modify the count, closing the race window.

Proof of Concept

Run this in the browser console while authenticated with a user that has 24 API keys:

(async () => {
    const makeKey = (desc) => fetch('/api/client/account/api-keys', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
        'X-XSRF-TOKEN':
  decodeURIComponent(document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1]),
      },
      body: JSON.stringify({ description: desc, allowed_ips: [] }),
    });

    const [r1, r2] = await Promise.all([makeKey('0024'), makeKey('0025')]);
    console.log('0024:', r1.status, (await r1.text()).slice(0, 200));
    console.log('0025:', r2.status, (await r2.text()).slice(0, 200));
})();

On the old code, both requests can return 200 (you may need to run this a few times to hit the race window). After the fix, the second request correctly returns a 400 error.

@DaneEveritt DaneEveritt merged commit a94b8bd into pterodactyl:1.0-develop May 23, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants