From f6810fd03bff0367973b8584b3b45b1d7030be0a Mon Sep 17 00:00:00 2001 From: Felix Ehlers Date: Mon, 29 Dec 2025 06:17:07 +0100 Subject: [PATCH] x.sessions: verify HMAC signatures when extracting sessions IDs from cookies (#26199) --- vlib/x/sessions/sessions.v | 10 +++++-- vlib/x/sessions/tests/session_test.v | 41 +++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/vlib/x/sessions/sessions.v b/vlib/x/sessions/sessions.v index 82f08fde5..2e7f41f1d 100644 --- a/vlib/x/sessions/sessions.v +++ b/vlib/x/sessions/sessions.v @@ -170,15 +170,19 @@ pub fn (mut s Sessions[T]) resave[X](mut ctx X, data T) ! { } // get_session_id retrieves the current session id, if it is set. +// The HMAC signature is verified when extracting from cookies. pub fn (s &Sessions[T]) get_session_id[X](ctx X) ?string { // first check session id from `ctx` sid_from_ctx := ctx.CurrentSession.session_id if sid_from_ctx != '' { return sid_from_ctx } else if cookie := ctx.get_cookie(s.cookie_options.cookie_name) { - // check request headers for the session_id cookie - a := cookie.split('.') - return a[0] + // check request headers for the session_id cookie and verify HMAC signature + sid, valid := verify_session_id(cookie, s.secret) + if valid { + return sid + } + return none } else { // check the Set-Cookie headers on the response for a session id for cookie in ctx.res.cookies() { diff --git a/vlib/x/sessions/tests/session_test.v b/vlib/x/sessions/tests/session_test.v index 435cc9be1..f377ba395 100644 --- a/vlib/x/sessions/tests/session_test.v +++ b/vlib/x/sessions/tests/session_test.v @@ -19,5 +19,44 @@ fn test_session_id() { verified_sid, valid := sessions.verify_session_id(sid_with_hmac, secret) assert unverified_sid == verified_sid - assert valid == true + assert valid +} + +fn test_forged_signature_rejected() { + // Create a valid session ID + sid, _ := sessions.new_session_id(secret) + + // Forge a cookie with valid session ID but invalid signature + forged_cookie := '${sid}.INVALID_SIGNATURE' + verified_sid, valid := sessions.verify_session_id(forged_cookie, secret) + + // Forged signature must be rejected + assert !valid + assert verified_sid == sid +} + +fn test_wrong_secret_rejected() { + // Create a session with one secret + _, signed_cookie := sessions.new_session_id(secret) + + // Try to verify with a different secret + wrong_secret := 'wrong_secret'.bytes() + _, valid := sessions.verify_session_id(signed_cookie, wrong_secret) + + // Must be rejected when using wrong secret + assert !valid +} + +fn test_malformed_cookie_rejected() { + // Cookie without signature separator + _, valid1 := sessions.verify_session_id('just_a_session_id', secret) + assert !valid1 + + // Empty cookie + _, valid2 := sessions.verify_session_id('', secret) + assert !valid2 + + // Cookie with empty parts + _, valid3 := sessions.verify_session_id('.', secret) + assert !valid3 } -- 2.39.5