---
title: "A 6,375 Byte Response Showed Us the Login Exemption Was Wrong"
canonical: https://dxdev.com/blog/the-security-fix-that-broke-the-front-door/
datePublished: 2026-07-30
---
One line was sitting in the staff session check: `if("login" == p) return;`. We were in the codebase for an unrelated ticket, tracing how staff sessions get verified on an internal admin tool, and it made the page redirect logic we'd come to read stop mattering. That's the login exemption, and it runs before the session check. `p` is a raw query-string parameter. Append `?p=login` to any tool URL and the gate returns without ever looking at whether you're staff.

We didn't take that on faith. We hit it live. An anonymous GET to a diagnostic tool with no cookies came back 302 to the login page, as expected. The same request with `?p=login` appended came back 200, with 6,375 bytes of live database rows in the body, cart items with real IDs and statuses. The same bypass worked against a second, unrelated tool. Then we counted how many pages shared this gate: 103 files under `tools/`, another 122 staff pages outside it, and 46 more under `ajax/`, all routed through the same `SessionStaffVerifyRedirect()` function. One missing check in one shared include left the entire internal admin surface open to anyone who knew the parameter name.

The exemption existed for a real reason. Without it, the login page would redirect-loop, since rendering it also runs the session check that says you're not logged in. But it was keyed on a value the client sends, and the server has no say in that value. The fix was to stop trusting `p` and check the resolved path instead, `session.pathInfo`, which IIS sets from the actual file being served and which a query string can't touch. Only `default.asp`, the login page itself, would get the exemption.

We patched a clone, then differential-tested it against the unpatched one side by side. The patched tool with `?p=login` now 302s to login, and the unpatched one still leaks 200 and data. The login page itself still rendered fine. We shipped it as a hotfix, verified the bypass was closed on production the same hour, and confirmed that staff who were already logged in saw no change. We closed the ticket and considered it clean.

## Thirteen hours later, correct passwords bounced

Staff couldn't log in. These were real login attempts with correct credentials, bouncing straight back to the login page with `error=SessionExpired` in the URL. We went to reproduce it and found we couldn't log into the staff portal on any clone either. A credential problem or a stale cache would be the usual suspect, and we burned a few minutes on exactly that before we looked at the actual response.

The credentials were fine. A POST to the login handler with deliberately wrong credentials still came back 302 to `error=SessionExpired` instead of reaching the actual authentication function. The guard was firing before `AuthenticateStaff` ever ran, so the password was never even checked. Our path-based fix from earlier that day only exempted `default.asp`, the page that renders the login form. But the form doesn't POST to `default.asp`. It posts to `/ajax/login.asp`, a different file, which runs through the exact same `SessionStaffVerifyRedirect()` on the way in. We'd fixed the render and broken the submit. We had reasoned about "the login page" as one thing when it was two files sharing one gate, and we only checked one of them.

Nobody had been locked out yet when we found it, because anyone with an existing session was unaffected, and staff who'd logged in before the hotfix just kept working. It stayed quiet by luck, not because it was working.

The fix had the same shape as the first one, just wider. We exempted both `default.asp` and `ajax/login.asp` by path, still keyed off `session.pathInfo`, still not spoofable by a query param. We verified it the same way as before. A bad-credentials POST now reached `AuthenticateStaff` and came back `error=invalidLogin` instead of `SessionExpired`. A real staff login succeeded end to end. The original `?p=login` bypass on an unrelated tool still redirected, so no regression. We verified live on prod before calling it closed, the same way we'd verified the hole in the first place, because "we're pretty sure" was exactly the assumption that put us here.

## How the review question was framed

The review that found this started from a question nobody could answer cleanly: where, exactly, does the login check happen on this surface? An answer like "it's probably fine because everything routes through the same shared include" didn't count. It was filed as a read-only investigation with no code change expected. It found that the one exemption in the check, there so the login page could render, was conditioned on a query-string value, which whoever sends the request controls. The fix checks the file the server resolved and is about to serve.

## Every entry point gets its own accounting

Every path that reaches the auth gate needs its own accounting of what it must do when there is no session yet, including the login handler whose whole job is to create one. When a shared gate changes, test each entry point live, with both a failed and a successful login, before calling the fix done.
