The admin order update endpoint authenticates via cookie and validates nothing else, allowing any same-session page to flip an order's status on the admin's behalf.
The handler performs three operations:
- Reads the
authTokencookie and resolves the current user. - Verifies the user has the
ADMINrole. - Updates the order with the supplied status.
Nothing sits between steps 1 and 3 to prove the request actually came from the admin's UI. The endpoint treats authentication as authorization. With sameSite: "lax" on the auth cookie, the browser also attaches it on top-level cross-site navigations and on every same-origin request, which is all an attacker page needs.
Exploitation
The lab serves the attacker page from the same origin (/exploits/csrf-attack.html) so the exploit works without setting up DNS or hosting. The same exploit works from a third-party origin too, either by carrying the request through a top-level navigation or against any deployment that loosens sameSite.
Step 1: Authenticate as an administrator
Sign in with an account that has the ADMIN role. If no such account is available, escalate using one of the other vulnerabilities in the lab (mass assignment on registration, JWT weak secret, etc.). After login, the browser holds an HTTP-only authToken cookie scoped to the application origin.
Step 2: Identify a target order
Open /admin and pick an order to manipulate. The walkthrough uses ORD-003 in PENDING status as the target. Any order will work; the flag is not tied to a specific identifier.
Step 3: Locate the exploit page
View the page source of /admin. A hidden link points to:
/exploits/csrf-attack.html
This file ships with the lab and simulates a phishing page that an attacker would normally host on a third-party domain.
Vulnerable code analysis
The handler authenticates the user and checks the role, then writes:
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const user = await getAuthenticatedUser(request);
// No CSRF token validation
// No Origin or Referer header check
const { status } = await request.json();
await prisma.order.update({ where: { id }, data: { status } });
}
The cookie configuration makes things worse:
response.cookies.set("authToken", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7,
path: "/",
});
httpOnly: true blocks JavaScript reads, which helps against token theft via XSS. It does nothing against CSRF, because CSRF does not need to read the cookie — it just needs the browser to send it. The sameSite semantics:
| Value | Cross-site cookie behavior |
|---|---|
strict | Cookie never sent on cross-site requests, including top-level navigation. |
lax | Cookie sent on top-level navigations (GET) but not on cross-site fetch. |
none | Cookie always sent; requires secure: true. |
In this lab the exploit page is same-origin, so lax does not apply at all. Even on a real cross-site deployment, lax still permits cookies on top-level GET navigations and on any same-site context, so it is not on its own enough to stop CSRF.
Remediation
No single one of the controls below is enough; apply them together.
Tighten the authentication cookie
Set sameSite: "strict" on the cookie that authorizes state-changing operations. Strict keeps the cookie out of every cross-site context, top-level navigation included:
response.cookies.set("authToken", token, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 60 * 60 * 24 * 7,
path: "/",
});
If strict breaks legitimate flows like email links landing on an authenticated page, split the session: a long-lived lax cookie for navigation and a separate strict cookie required for sensitive endpoints.
Require a CSRF token on state-changing routes
Issue a per-session token at login, hand it to the client via a non-httpOnly cookie or a bootstrap endpoint, and require the client to echo it back in a custom header:
const tokenFromCookie = request.cookies.get("csrfToken")?.value;
const tokenFromHeader = request.headers.get("X-CSRF-Token");
if (!tokenFromCookie || tokenFromCookie !== tokenFromHeader) {
return NextResponse.json({ error: "Invalid CSRF token" }, { status: 403 });
}
A cross-origin attacker page cannot read cookies for the target origin, and it cannot set custom headers on a cross-origin request without a passing CORS preflight. It can supply at most one half of the pair.
Validate the Origin header
For non-GET requests, reject anything whose Origin (or, failing that, Referer) is not on an explicit allowlist:
const origin = request.headers.get("origin");
const allowedOrigins = ["https://yourdomain.com"];
if (!origin || !allowedOrigins.includes(origin)) {
return NextResponse.json({ error: "Invalid origin" }, { status: 403 });
}
The check is cheap and runs before any business logic. It catches most cross-origin CSRF attempts even when the token-based defense is misconfigured or partially deployed.
Lab
Security training for the apps you actually ship. Open your browser and start hacking.
OSS - OopsSec Store
An intentionally vulnerable e-commerce app for learning web security.
Master real-world attack vectors through a realistic CTF platform.
Hunt for flags, exploit vulnerabilities, and level up your security skills
·
·
References
Disclaimers
Do not deploy OopsSec Store on a production server. This application is intentionally vulnerable and should only be used in isolated, local environments for educational purposes.
Do not exploit vulnerabilities on systems you don’t have explicit authorization to test. Unauthorized access to computer systems is illegal. Always obtain proper permission before performing security testing.
Feedback & Support
Having trouble following this writeup? Found a typo or have suggestions for improvement?
Feel free to open an issue or start a discussion on GitHub.
Community-Analysen & Experten-Meinungen 0
Verwandte Story-Cluster & Quellen (Vektor-KI)
Ähnliche Beiträge
Auch interessante Nachrichten Why sameSite: "lax" doesn't save your Next.js admin routes from CSRF
Thematisch verwandte Begriffe: sameSite, doesnt, save, your · 6 Treffer
How I Turned Self-XSS into Reflected XSS (and Bypassed the WAF)
Roundcube Fixes 12 Security Flaws Including Zero-Click XSS and SSRF Bypass
Telerik UI Flaws Let Attackers Chain AES-CBC Padding Oracle to Unauthenticated RCE
Stopping Vulnerable Driver Attacks
Videos werden geladen ...
Beiträge werden geladen ...
Videos werden geladen ...
Beiträge werden geladen ...
Videos werden geladen ...
Beiträge werden geladen ...
Videos werden geladen ...
Beiträge werden geladen ...
Videos werden geladen ...
SOCIAL SHARE CARD GENERATOR