Signed In as Somebody Else — JavaScript Bug Hunt
Modelled on GitHub, March 2021. GitHub disclosed that a rare race condition in its request handling could, under specific conditions, send one user's…
- Language: JavaScript
- Layer: Backend
- Difficulty: Hard
- Concepts: Concurrency, Auth, State
- Modelled on: GitHub · 2021
- Visible tests: one request at a time works; interleaved requests keep their own sessions
- Reward: 50 XP for a complete fix
Briefing
Modelled on GitHub, March 2021. GitHub disclosed that a rare race condition in its request handling could, under specific conditions, send one user's session cookie back in the response to another user's request — leaving that browser signed in as someone else. GitHub fixed the bug and invalidated every signed-in session on github.com, signing everyone out.
This reconstruction's worker splits a request into begin (authenticate) and finish (render the response) so other requests can run in between, and keeps the authenticated session in a variable shared by the whole worker. The tests drive the interleaving explicitly.
Fix the worker so each response carries its own request's session.
Bug report
BUG-GH-2103 · Priority: Critical (session leak) · Reported by: security
createWorker(sessions) returns { begin(req), finish(id), pending() }:
- begin({ id, cookie }) authenticates the request with sessions.lookup(cookie)
- finish(id) returns { id, user, setCookie } for THAT request: user is the session's name and setCookie its token, or both null for a request with no valid session — regardless of what began or finished in between
- finish(id) for an id that was never begun, or already finished, throws
- pending() is the number of begun but unfinished requests; a finished request leaves nothing behind
Observed: with two requests in flight, the first to finish was answered with the second one's session cookie.
Logs
[web] begin req=a cookie=tok-alice
[web] begin req=b cookie=tok-bob
[web] finish req=a user=bob set-cookie=tok-bobThe code as shipped
src/web/worker.js (editable)
exports.createWorker = function (sessions) {
var current = null;
var inFlight = 0;
return {
begin: function (req) {
current = sessions.lookup(req.cookie);
inFlight++;
},
finish: function (id) {
inFlight--;
return {
id: id,
user: current ? current.name : null,
setCookie: current ? current.token : null
};
},
pending: function () {
return inFlight;
}
};
};
Read-only context: src/web/sessions.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.