Commit a94b8bd
authored
Fix API Key Limit Race Condition Bypass (#5620)
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:
```js
(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.1 parent 7ffcd63 commit a94b8bd
1 file changed
Lines changed: 10 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
32 | | - | |
33 | | - | |
34 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
35 | 37 | | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
| |||
0 commit comments