SECURE_NODE // ATHENS_GR
← BACK TO RESEARCH Technique · Auth · Session Management

Session Not Rebound on Role Switch

03 JUL 2026| ≈ 4 MIN READ| SessionManagementSessionRebindingRoleConfusionBrokenAuth
SANITIZED ▸ Methodology only. No client name, real host, real data, or live finding. Reproduced against a lab environment.

Session fixation is a well-known class of vulnerability where an attacker forces a known session ID onto a victim before they authenticate, then hijacks the now-elevated session. This finding is subtly different, and worth distinguishing carefully, because the root cause and the fix are not the same. What we found was an application that issued fresh session IDs on each login, so classic fixation was not technically in play. The problem was that the session was never properly bound to the authenticated identity. Logging in under a different role in the same browser caused requests from the original session to execute under the newly authenticated context, including an admin-privileged one, with no indication to either party that this was happening.

SEVERITY
Medium
role confusion / session management
COMPLEXITY
High
same-browser scenario
ROOT CAUSE
No session rebind on login
context bleeds across roles

What the application was doing wrong

The application exposed two distinct user interfaces: an admin panel and a customer-facing interface. Both were served from the same origin and shared the same session mechanism. On login, the server generated a new session ID, which was correct on its own. But the session was not bound to a specific user identity or role server-side. Instead, the server tracked only the most recently authenticated context.

The consequence: if an admin logged in first and then the same browser authenticated again as a customer (in a new tab, without explicitly logging out), the customer-tab requests carried the original session ID. That session now reflected the admin context. The last privileged login had overwritten the binding. Requests from the customer tab silently executed with admin privileges.

The attack surface is narrower than full session fixation because an attacker cannot preset the session from outside the browser. But any scenario where the same device is used for both roles, such as support staff checking their own customer account or a developer testing a flow, produces the exposure. That precondition is what keeps this a moderate-severity issue rather than a straightforward remote escalation: the impact is real when the scenario fires, but it cannot be triggered by an outside attacker acting alone.

Steps to reproduce

STEP 1 · LOG IN AS ADMIN

Open a browser and authenticate to the admin panel. Note the current session ID. Keep this tab open.

STEP 2 · LOG IN AS CUSTOMER IN A NEW TAB

Without logging out, open a new tab and authenticate to the customer interface. The server issues a new session ID for this tab.

STEP 3 · RETURN TO THE ADMIN TAB

Make any authenticated admin request from the original tab. Observe that the request now executes within the context of the most recently authenticated user, the customer login, breaking the admin operation. Or, in the other direction:

STEP 3 (REVERSE) · ADMIN AFTER CUSTOMER

Log in as admin after a customer login. Navigate the customer tab to an admin resource. The request is fulfilled with admin privileges, even though the tab's visible session corresponds to a customer account.

STEP 4 · CONFIRM

Confirm role confusion. The customer-tab user can access admin-only responses, or the admin-tab user receives customer-scoped data where admin data was expected, depending on which direction the re-authentication occurred.

The request flow

The following sequence reproduces the higher-impact direction: a customer-facing tab ends up carrying an admin session after the admin re-authenticates in the same browser.

Customer logs in first, and the browser receives session-A:

POST /customer/login/ HTTP/2
Host: store.lab.example.test
Content-Type: application/x-www-form-urlencoded

action=login&email=customer@store.lab.example.test&password=[redacted]&__csrf__=csrf_lab_token

HTTP/2 302 Found
Location: https://store.lab.example.test/
Set-Cookie: PHPSESSID=<session-A>; path=/; secure; HttpOnly
# session-A active; customer context, customer-facing tab open

Admin authenticates in a new tab. The server issues a fresh session ID and the browser updates the shared cookie jar, so all open tabs now carry session-B:

POST /admin/login/ HTTP/2
Host: store.lab.example.test
Content-Type: application/x-www-form-urlencoded

action=login&email=admin@store.lab.example.test&password=[redacted]&__csrf__=csrf_lab_token

HTTP/2 302 Found
Location: https://store.lab.example.test/admin/
Set-Cookie: PHPSESSID=<session-B>; path=/; secure; HttpOnly
# session-A replaced in the shared cookie jar; customer tab now sends session-B

Back in the customer tab, a navigation to an admin-only endpoint succeeds. The tab is unknowingly carrying the admin session established by the admin login in the other tab:

GET /admin/users/ HTTP/2
Host: store.lab.example.test
Cookie: PHPSESSID=<session-B>

HTTP/2 200 OK
Content-Type: application/json
# admin endpoint returns full user list; request originated from the customer tab

Fixation vs. rebinding, and why the distinction matters

Classic session fixation requires an attacker to set the victim's session ID before the victim authenticates. The fix is to rotate the session ID on every login event. This application does rotate the ID, so the classic fix was already partially in place. The missing piece is binding: associating the session with a specific user identity and role, server-side, and rejecting requests where the session's bound identity does not match the requested resource's access control requirements.

Without that binding, rotating the session ID on login only solves the fixation path. It does nothing when the same physical browser logs into two roles sequentially and the server lets the most recently authenticated context win globally.

Fixing it

  • Bind every session to a specific user ID and role at login time. Store the binding server-side. A session that was issued for user A should never serve requests intended for user B, regardless of which tab made the request.
  • Invalidate and rotate the session on every new login event, not just the first. A second login in the same browser should terminate the existing session for the first identity before issuing a new one for the second.
  • Enforce role checks at the resource level, not just at login. Even if session rebinding is corrected, an authorisation layer that validates the session's bound role on each request provides defence-in-depth and catches future session management regressions.
  • Separate session namespaces for admin and customer interfaces. Using distinct cookie names or paths for admin and customer sessions prevents them from sharing the same session storage context and limits the blast radius of any future session management flaw.

The takeaway

"We rotate the session on login" is a start, not a finish. A session ID is only as meaningful as the identity it is bound to. If the server tracks authentication state globally rather than per-identity, rotating the identifier solves the token-prediction problem while leaving the trust-confusion problem wide open.