Repository navigation
Don't adopt unknown session IDs from the request cookie - #2647
Open
BhavayGoyal wants to merge 2 commits into
Open
BhavayGoyal wants to merge 2 commits into
BhavayGoyal wants to merge 2 commits into
Conversation
findSessionForRequest() passed any non-empty session cookie value to SessionManager::getSession(), which created a session under that key if none existed. A client could therefore pick its own session IDs: session fixation, unbounded growth of the session cache, and keys chosen to collide in the cache's unordered_map. When the ID comes from the cookie and does not name a live session, create the session under a new server-generated ID instead. It is marked to be sent to the client, so the cookie is replaced on the response.
CI failures on the PR branch, both pre-existing in the test added here: - format: clang-format-17 reformats the lambda-taking constructor call in SessionManagerTest.cc to one line plus a hanging block, not the manually wrapped one it was written with. - windows/msvc SHARED: the unit test links against drogon.dll and calls SessionManager's constructor and getSession() directly, but SessionManager (lib/src, internal) was never marked DROGON_EXPORT, so those symbols aren't in the DLL's export table on a Windows shared build -- LNK2019/LNK1120. MultipartStreamParser and HttpResponseImpl, the other lib/src classes with direct unit tests, already use this pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2593
Problem
With sessions enabled,
findSessionForRequest()hands any non-emptyJSESSIONIDcookie value toSessionManager::getSession(), andgetSession()creates a new session under that key when it isn't in the cache. So the client chooses the session ID:std::unordered_mapbehindCacheMap, which is the HashDoS described in the issue.Fix
In
SessionManager::getSession(), when the ID came from the cookie (needToSet == false):needToSet = true, sohandleSessionForResponse()sends the new cookie.So only server-generated IDs ever become cache keys. A client whose session expired (or who comes back after a server restart) just gets a new cookie, as it would without a cookie at all. Lookup of an existing session still refreshes its timeout as before (
findAndFetch, likemodify).This covers the first suggestion in the issue. I didn't add a keyed hash or a session count cap here, to keep the change small; with only server-generated IDs as keys, clients can no longer pick colliding keys.
Testing
SessionManagerTestunit test: an issued ID returns the same session; an unknown ID gets a new session with a fresh server-generated ID; that new ID then resolves; the unknown ID is never stored. Against the current code it fails 5 of 9 assertions (the attacker-chosen ID is adopted); with the fix all pass.unittestsuite passes (73 test cases, Ubuntu 24.04, GCC), andformat.shwith clang-format-17 leaves no diff.(Written with Claude, working with @BhavayGoyal.)