---
title: "I Closed an Unauthenticated Bypass, Then Locked Out My Own Staff"
canonical: https://dxdev.com/blog/2026-09-07_the-security-fix-that-broke-the-front-door/
datePublished: 2026-07-29
---
The alert I was chasing had nothing to do with what I found. I was in the codebase for an unrelated ticket, tracing how staff sessions get verified on an internal admin tool, when I hit a line that made the whole page redirect logic stop mattering: `if("login" == p) return;`. That's the login exemption, checked before the session check, and `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.

I didn't take that on faith. I 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. Same bypass worked against a second, unrelated tool. Then I checked how many pages shared this gate: 103 files under `tools/`, another 122 staff pages outside it, 46 more under `ajax/`, all routed through the same `SessionStaffVerifyRedirect()` function. One missing check, one shared include, 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 itself 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, not on anything the server actually controls. 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.

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

## The guard didn't know it was also blocking the door it guarded

Thirteen hours later, staff couldn't log in. Not "session expired," not a typo, actual login attempts, correct credentials, bouncing straight back to the login page with `error=SessionExpired` in the URL. I went to reproduce it and found I couldn't log into the staff portal on any clone either, which is not something I'd normally suspect from a same-day hotfix, so I burned a few minutes wondering if it was a credential or cache issue before I looked at the actual response.

It wasn't the credentials. 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, meaning the password was never even checked. My 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. I'd fixed the render and broken the submit, because I'd reasoned about "the login page" as one thing when it was two files sharing one gate, and I only checked one of them.

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

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

The mistake wasn't skipping a test. It was treating "authenticate before serving" as one rule when the real rule is per-entry-point: every path that reaches the auth gate needs its own accounting of what it's supposed to do when there's no session yet, including the one thing whose whole job is to create one.
