Auth & Session Attacks
Authentication proves who you are, authorization decides what you may do, and sessions carry that identity between requests. Learn the attacks against each link — credential stuffing, brute force, session fixation, IDOR — and the defenses: strong hashing, rotation, secure cookies and server-side ownership checks.
What is this?
Authentication verifies who a user is, authorization verifies what that user may do, and sessions carry identity across requests. Attacks against this layer target the trust an application places in an authenticated identity — credential stuffing and brute force on login, token theft and fixation on sessions, and missing ownership checks on resources. Even strong passwords fail if tokens are guessable, sessions never rotate or expire, or authorization checks are missing.
Why do you need it?
- A broken authentication flaw turns one valid account — or an attacker's fake one — into a route to sensitive data.
- The session token is the key to the kingdom: its theft or fixation equals full account takeover.
- Authorization gaps (IDOR and missing function-level checks) have ranked as the top web risk in recent Top 10 data.
- Frameworks like OWASP ASVS and PCI-DSS dedicate entire sections to authentication and session management.
- Every other control sits on top of identity, so a flaw here amplifies every other weakness in the system.
Mental model
A building badge and a door guard
A session token and an authorization check
The badge proves you entered the building, but the guard still checks whether you may enter the server room. A system that trusts only the badge lets any badge holder into every room — the same failure as trusting only the session cookie without per-request authorization.
How it works
- The user presents credentials; the server verifies them against the password hash and issues an opaque, random token or session ID.
- The client sends the token with every request and the server resolves it back to a user.
- Authorization middleware checks the resolved identity against the resource's ownership and role rules.
- Attacks target each link: brute-forcing credentials, predicting or stealing tokens, fixing a session, or skipping the check entirely.
- Defense: strong password hashing, rate limiting, short-lived rotating tokens, secure cookie flags, and ownership checks on every resource.
The identity chain from login to a returned resource.
Key commands
curl -s -i -X POST "http://sandbox.local/login" -d "username=alice&password=CorrectHorse1"Capture the Set-Cookie header and inspect its HttpOnly, SameSite and Secure flags.
Set-Cookie: session=...; HttpOnly; SameSite=strict; Secure; Max-Age=1800
curl -s -i -H "Cookie: <session-from-login>" "http://sandbox.local/api/invoices/1"Probe object-level authorization using a session that does not own the resource.
openssl rand -hex 32Generate a token with the entropy a good session ID should have.
a3f2c1...e9d8b4c5a6f7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5e6d7c8b9a0f1e2d3
grep -rn "req.cookies.session\|req.headers.authorization" src/Audit how the session token is resolved on every request path.
hashcat -a 0 -m 0 hashes.txt rockyou.txtEducational offline-hash cracking demo — only run against hashes you own to understand why fast hashes fail.
Examples
Secure login, session cookies and an ownership check
const bcrypt = require("bcryptjs")const crypto = require("crypto") // Login: hash comparison never exposes whether the user exists.app.post("/api/login", async (req, res) => { const user = await db.users.findByEmail(req.body.email) const ok = user && (await bcrypt.compare(req.body.password, user.password_hash)) if (!ok) return res.status(401).json({ error: "invalid credentials" }) const token = crypto.randomBytes(32).toString("hex") await db.sessions.create({ userId: user.id, token }) res.cookie("session", token, { httpOnly: true, sameSite: "strict", secure: true, maxAge: 30 * 60 * 1000, }) res.json({ ok: true })}) // Authorization: ownership + role check on every object access.app.get("/api/invoices/:id", requireAuth, async (req, res) => { const invoice = await db.invoices.findById(req.params.id) if (!invoice) return res.status(404).json({ error: "not found" }) if (invoice.userId !== req.user.id && req.user.role !== "admin") { return res.status(403).json({ error: "forbidden" }) } res.json(invoice)})Three controls work together. bcrypt.compare runs even when no user exists, so response timing does not reveal whether an email is registered. The session token is 32 random bytes stored server-side and delivered as an HttpOnly, SameSite=strict, Secure cookie with a 30-minute lifetime, so it cannot be read by JavaScript, leaked by cross-site requests or sniffed. Finally, the invoice handler re-checks ownership and role on every request, which is the defense against the IDOR attacks covered in this module.
In production
- Sessions are short-lived, rotate on privilege change and are invalidated on logout.
- Multi-factor authentication is enforced for privileged accounts and risky actions.
- Cookies carry HttpOnly, Secure and SameSite attributes so tokens are not readable by scripts.
- Authorization checks run server-side on every request, never only in the frontend.
Best practices
- Store passwords with bcrypt or argon2, a per-user salt and a high work factor.
- Use a random, opaque session token; never encode user data into a client-readable token.
- Set cookies with HttpOnly, Secure, SameSite and an explicit expiry.
- Rate-limit login, apply account lockout with backoff, and rotate tokens after login and password change.
- Apply ownership and role checks server-side on every resource endpoint.
Common mistakes
- Trusting the frontend to hide admin buttons while the API skips authorization.
- Storing passwords as plain text, md5 or a fast hash like sha256.
- Never invalidating sessions on logout or password change.
- Using a JWT with a weak secret or an absurdly long lifetime.
Common issues
Session cookie is not set in the browserThe cookie lacks the Secure flag while the site is served over plain HTTP, or SameSite conflicts with the request.
Test over HTTPS, or set secure: false in the sandbox only; confirm the domain and path match the request URL.
401 even after a successful loginThe client is not sending the token, or the server expects a different header name.
Confirm the cookie/header name in the resolve code matches what the client sends.
User can read another user's recordsA missing ownership check in the handler.
Add an owner/role check (see the example), then re-run the IDOR test with a second account.
Debugging tips
- Inspect the Set-Cookie header in a proxy to confirm HttpOnly, Secure and SameSite flags.
- Check whether the session ID changes after login (fixation test) and after password change (rotation test).
- Log authorization denials server-side to see whether the checks are actually being reached.
Interview checkpoint
Hands-on lab
- Bypass a weak authentication flow in the sandbox
- Demonstrate a session-fixation scenario and confirm the flaw
- Probe an object-level authorization endpoint with two accounts
- Harden the flow with rotation, cookie flags and an owner check
Auth Bypass & Session Fixation in WebGoat
45 min · advanced
Quick quiz
Which cookie attribute prevents JavaScript from reading the session token?
Storing a session token in localStorage is safe as long as the token expires quickly.
You log in twice from the same browser and the session ID does not change after the second login. Which attack does this enable?
Cheat sheet
bcrypt.compare(pw, hash)Slow hash, constant-time comparecrypto.randomBytes(32).toString('hex')Opaque, unguessable tokenhttpOnly: true, sameSite: 'strict', secure: trueCookie flagsrotate session on loginDefeats fixationrotate on privilege changeDefeats privilege escalation reuseowner + role check per requestDefeats IDOR