15.7 Authentication Attacks
Table of Contents
Understanding Authentication Attacks
Authentication attacks try to break the “who are you” part of security. They target login, signup, reset password, or any place where users prove their identity. In this chapter you will see how attackers think about authentication, what common attacks look like, and which backend mistakes make them possible.
We will not fully re-explain password hashing, tokens, or OAuth here, since those have their own chapters. Here the focus is how those mechanisms can be attacked, and what you should watch out for when you build a backend.
The Attacker’s Goal
An attacker usually wants to:
- Log in as another user, preferably an admin.
- Create a new powerful account without proper checks.
- Bypass authentication completely.
- Steal reusable credentials, such as passwords, tokens, or cookies.
They do not always need your database. If they can act as a victim’s session in your API, for that victim the effect is the same as “my account was hacked.”
You can think of authentication as two questions:
- “Who are you?” (identity)
- “Prove it.” (credential)
Authentication attacks try to either guess or steal the proof, or skip that proof step entirely.
Credential Stuffing
Credential stuffing uses real username and password pairs that leaked from other sites. Attackers assume users reuse passwords everywhere.
Example:
- A breach exposes
user@example.com / Passw0rd!from Site A. - The attacker has a list of millions of such combos.
- They try those combos automatically against your login API.
- Some of your users reused the same password, so some logins succeed.
How Credential Stuffing Looks in Logs
You might see patterns like:
- Many login attempts from a few IPs.
- Different usernames each time.
- Each username tried only once or a few times.
- Usernames that look like real emails, not random letters.
Example sequence:
POST /api/login
body: {"email": "alice@gmail.com", "password": "Sunshine123"}
POST /api/login
body: {"email": "bob@yahoo.com", "password": "Password123"}
POST /api/login
body: {"email": "charlie@outlook.com", "password": "Qwerty1!"}Same IP, different users, each tried once. This is very different from brute force, where many attempts target the same user.
Why Credential Stuffing Works
Main reasons:
- Password reuse by users.
- No rate limiting on login.
- No IP-based or user-based anomaly detection.
- Generic login error messages that tell only “invalid username or password”, which does not slow the attacker.
Rule: Assume your users reuse passwords. Your job is to make stolen credentials from other sites much less effective against your backend.
Defenses Specific to Credential Stuffing
You will cover the details in other chapters, but for credential stuffing pay attention to:
- Rate limiting login endpoints per IP and per account.
- Temporary account lockouts or stronger challenges after several failures (with care to avoid easy account lockout attacks).
- IP reputation and blocking, especially for known bad IP ranges or anonymous proxies.
- Login anomaly detection like:
- Impossible travel (logins from distant countries in a short time).
- Sudden spikes of failures across many accounts.
- Step-up authentication after suspicious login, such as extra verification.
Brute-Force and Password Guessing
Brute-force attacks try to guess a user’s password by trying many possibilities. These can be:
- Naive brute-force: trying all possibilities, for example all 8 character combinations.
- Dictionary attack: trying likely passwords from a password list.
- Targeted guessing: using information about the victim, for example birthdate or pet name.
Brute-Force Patterns
Typical brute-force uses multiple attempts against the same account:
POST /api/login
{"username": "alice", "password": "123456"}
POST /api/login
{"username": "alice", "password": "alice123"}
POST /api/login
{"username": "alice", "password": "Password123"}
POST /api/login
{"username": "alice", "password": "A1ice!2024"}This might be done from one IP or many IPs to hide the pattern.
Account Enumeration via Error Messages
A mistake that helps brute-force is telling attackers which usernames exist.
Bad design:
- If the username is wrong:
404 User not found - If the password is wrong:
401 Incorrect password
Now the attacker can:
- Enumerate valid usernames using the message or status code.
- Brute-force only the valid accounts.
Better:
- Always respond with the same generic message such as
"Invalid username or password"and the same status code, for example 401.
Rule: Do not reveal whether a username or email exists during login, password reset, or registration checks.
Simple Brute-Force Protections
Common protections you implement on the backend:
- Rate limiting per:
- IP address.
- Username or email.
- Progressive delays:
- After 3 failed attempts for an account, slow down responses for that account.
- Account lockout or cool-down:
- After N failed logins, require a cool-down period or an out-of-band verification.
- Strong password policy and checks against known-breached passwords.
Example of progressive delays in pseudocode:
def login(user_id, password):
if failed_attempts_for(user_id) > 5:
time.sleep(2) # Slow down brute force
if not check_password(user_id, password):
record_failed_attempt(user_id)
return error("Invalid username or password")
reset_failed_attempts(user_id)
return success()Be careful: account lockout can itself be abused as a denial of service. If locking happens too quickly or lasts too long, an attacker can keep users locked out.
Password Spraying
Password spraying is similar to brute-force, but with a twist:
- Brute-force: many passwords against one user.
- Spraying: one or a few common passwords across many users.
Example:
Try password "Summer2024!" for each user in the organization list:
alice@company.com
bob@company.com
carol@company.com
...Advantages for attackers:
- Fewer failed attempts per user, so many simple lockout systems will not trigger.
- Many users choose the same weak passwords.
Detecting Password Spraying
Indicators:
- Few attempts per user, but each attempt uses very common passwords.
- The same password attempted across many accounts from one IP or subnet.
- Periodic activity that spreads attempts over time, such as once every few minutes.
Defenses are similar to brute-force, but you should also:
- Enforce minimum password complexity and avoid common passwords.
- Use breached password checks, for example rejecting passwords that appear in known leaks.
- Monitor failed login patterns across multiple accounts, not only per user.
Phishing and Social Engineering
Phishing is not a pure backend attack, but it often leads to valid credentials being used against your backend. Attackers trick users into:
- Entering their username and password on a fake login page.
- Approving a real login or OAuth consent they do not understand.
- Sharing one-time codes by phone or chat.
Once the attacker has valid credentials, your backend will consider them legitimate unless you add extra protections.
How Backend Design Influences Phishing Impact
Even though phishing is a user-side problem, backend decisions affect damage:
- Session management:
- Short-lived access tokens limit how long stolen tokens can be used.
- Multi-factor authentication (MFA):
- Even if a password is phished, the attacker still needs a second factor.
- Login alerts:
- Notify users on new device or new location logins.
- Device or IP recognition:
- Mark trusted devices and apply extra checks for new devices.
Example: if your API issues never-expiring bearer tokens, and a user is phished, the attacker has long-term access. If instead your tokens expire quickly and refresh tokens are tied to a specific device, the damage is reduced.
Session Hijacking
Session hijacking is when an attacker gets hold of a valid session identifier and uses it to impersonate the rightful user. For a web backend that can be:
- A session cookie value.
- A bearer access token stored in local storage.
- An API key that acts like a session.
How Attackers Get Session IDs
Common paths:
- XSS steals cookies or tokens from the browser.
- Unencrypted HTTP traffic lets attackers read cookies over the network.
- Logs or error traces accidentally contain session IDs.
- Insecure redirects or query strings expose tokens in URLs.
- Shared devices without logout, where someone local reuses an open session.
Example: Cookie-Based Session
If your site sets:
Set-Cookie: sessionid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
An attacker who somehow obtains abc123 can send:
GET /account
Cookie: sessionid=abc123Your backend will treat this as the authenticated user.
Protecting Sessions
Key backend-side protections:
- Use Secure cookies so they are sent only over HTTPS.
- Use HttpOnly cookies to prevent JavaScript from reading them.
- Use SameSite to reduce CSRF-related cookie abuse.
- Rotate session IDs on login and privilege changes.
- Short session lifetimes with inactivity timeouts.
- Invalidate sessions server side on logout or password change.
Rule: Never treat any user-supplied session ID or token as safe. Always assume an attacker could send a stolen one and design with expiration, rotation, and invalidation.
Token Theft and Replay
Many modern backends use JWT or other bearer tokens. These tokens are often short strings that, if stolen, give full access for their lifetime.
A typical token-based flow:
- User logs in with username and password.
- Backend returns an access token, for example a JWT, and sometimes a refresh token.
- Client includes the access token in an
Authorization: Bearer <token>header.
If an attacker steals the token, they can perform any request that the real user can, until the token expires or is revoked.
Common Token Theft Paths
- Local storage or session storage in browsers accessible via XSS.
- In URLs such as
/callback?token=...in query parameters, which may be logged or leaked. - In logs where developers log full headers or request bodies.
- Over HTTP without TLS, allowing sniffing.
- On shared devices where tokens are cached.
Token Replay
Replay is when an attacker records a valid authentication step or token, then reuses it later.
Example:
- User makes a valid API call with
Authorization: Bearer X. - Attacker on the network captures this request.
- Attacker sends the same header to your API again.
- Request succeeds because the token is still valid.
Replay is easier if:
- Tokens live a long time.
- Tokens are not bound to device or client.
- There is no extra context check on the backend, for example IP or user agent.
Basic Defenses for Token-Based Systems
As a backend developer, you can:
- Use HTTPS everywhere so tokens are not visible in transit.
- Keep tokens short-lived:
- Access tokens with minutes-level expiry.
- Use refresh tokens instead of long-lived access tokens.
- Store tokens securely on clients:
- Prefer HttpOnly cookies when possible.
- Avoid tokens in URLs:
- Never in query strings.
- Do not log full authorization headers in production.
- Consider token revocation or blacklists:
- For high-risk actions or after logout.
- Bind tokens to context where appropriate:
- Check user agent or device id.
- For some systems, check approximate IP ranges.
Example of a safer JWT configuration conceptually:
expclaim set to 15 minutes.audandissclaims checked on the backend.- Rotating refresh tokens stored securely and revocable.
MFA Bypass and Weak Second Factors
Multi-factor authentication (MFA) combines:
- Something you know, like a password.
- Something you have, like a phone or hardware key.
- Something you are, like a fingerprint.
MFA reduces many authentication attacks, but if implemented poorly it can be bypassed.
Common Weaknesses
- SMS-based codes only, which can be intercepted via SIM swapping.
- Second factor only required sometimes, but not for sensitive actions like password change.
- Backup codes that are too easy to guess or never expire.
- Logic flaws where some API endpoints skip MFA checks.
Example logic flaw:
- Web app shows MFA prompt after password, but the API endpoint
/api/account/deleteonly checks that the access token is valid, not that MFA was done recently. If an attacker steals the token, they can delete the account without ever solving MFA.
Backend Responsibility for MFA
As the backend:
- Enforce MFA where security matters:
- Login events from new devices.
- High-risk operations, like changing email, password, or withdrawing funds.
- Check MFA state server side:
- For example, store
"mfa_verified_at"in the session and require it to be recent for sensitive actions. - Handle backup codes safely:
- Store them hashed, like passwords.
- Limit their use count.
- Provide a way to revoke them.
Logic Flaws in Authentication Flows
Not all attacks involve guessing or stealing credentials. Some attacks exploit mistakes in how you designed the authentication steps.
Example 1: Skipping Email Verification
Flow:
- User signs up with
user@example.com. - Backend creates user with
email_verified = false. - Verification email is sent with a token.
Bug:
- The login API ignores
email_verified. - Users can log in and use the system without verifying email.
Impact:
- Attackers can create many fake accounts.
- Features that rely on email ownership, such as password reset, become meaningless.
Example 2: Weak “Remember Me” Tokens
Flow:
- User logs in and checks “Remember me.”
- Backend sets a cookie
remember_token="12345". - On future visits, if
remember_tokenis present, you log them in automatically.
Bug:
remember_tokenis a short or predictable value, for example incrementing integer.- No server-side mapping or blacklist.
- No expiration.
Attack:
- Attacker guesses or brute-forces tokens such as
"12345","12346", etc. - Once they find a valid one, they log in as that user.
Safer approach:
- Generate long, random tokens.
- Store a hashed version with user id and expiration.
- Allow tokens to be revoked or rotated.
Example 3: Insecure “Magic Link” Login
Magic link login:
- User enters email.
- Backend sends a login link:
/magic-login?token=<random>. - When clicked, the token logs the user in.
Bug:
- Same token is valid multiple times.
- Token has a long expiration.
- Token appears in logs or referer headers.
If an attacker gets an old magic link URL, they can log in as the user. Your backend should ensure these tokens:
- Are one-time use.
- Expire quickly, for example in minutes.
- Are invalidated after first use.
Password Reset Attacks
Password reset is often the weakest link in authentication, since it provides a way to take over an account without the existing password.
Typical secure flow:
- User submits their email.
- Backend sends an email with a short-lived, random token in a reset link.
- User clicks the link, sets a new password.
- Backend invalidates the token and ends existing sessions.
Common mistakes:
- Tokens with no expiration.
- Predictable tokens, for example derived from user id.
- Reusable tokens.
- Tokens stored in plain text in a database and logged.
- Allowing password reset without revalidating the email or identity in some way.
Example of an insecure scheme:
Reset link:
https://example.com/reset?user_id=123&ts=1690000000Attacker guess:
- They try different
user_idvalues with current or recentts. - Backend does not validate any secret token, just id and timestamp.
Better:
Reset link:
https://example.com/reset?token=3fbb9e9a-66a4-4e6d-b27a-4466db5e7c33Backend stores:
| token_hash (SHA-256) | user_id | expires_at | used |
|---|---|---|---|
| a9f5... | 123 | 2026-08-27 10:30:00 | 0 |
On use:
- Hash the incoming token.
- Look up the hashed token.
- Check it exists, not expired, not used.
- Mark as used.
OAuth and Social Login Misuse
OAuth 2.0 and social logins like “Login with Google” or “Login with GitHub” reduce password handling on your side, but introduce new types of mistakes.
Typical secure sequence:
- Your app redirects the user to the provider with a client id, redirect URI, and state.
- User logs in at the provider and consents.
- Provider redirects back with a code.
- Your backend exchanges the code for tokens and retrieves the user profile.
- Your backend creates or finds a local account for that external id.
Common mistakes:
- No
stateparameter: - This can enable CSRF-like attacks where an attacker binds a victim’s session to their own provider account.
- Not verifying the ID token:
- For example, you do not check the audience or issuer.
- Trusting any email:
- You accept the email from the identity provider without checking domain rules or that it is verified.
Example attack scenario without state:
- Attacker logs in with their Google account via your app and sees the redirect URL.
- Attacker sends that URL to a victim and tricks them to click it.
- The victim’s browser sends the request, but the resulting session on your site is bound to the attacker’s Google identity.
- Confusing behavior or account confusion can follow, depending on implementation.
As a backend developer:
- Always validate OAuth tokens.
- Use and verify the
stateparameter. - Decide carefully how you link external identities to internal accounts.
API Keys and Shared Secrets
Some backends use API keys or shared secrets for machine-to-machine authentication. These are often long strings like:
sk_live_51JLv6bF2...If these keys are exposed, anyone who has the key can act as the authorized client.
Common exposure paths:
- Keys committed to public GitHub repositories.
- Keys in frontend code when they should only be on the backend.
- Keys in logs or error messages.
- Keys stored in screenshots or documentation.
As an attacker:
- Finding a leaked API key is as good as finding a password.
- They can call your APIs at scale.
From the backend side:
- Treat API keys as passwords.
- Provide a way to rotate keys and revoke compromised ones.
- Limit API keys to specific permissions and scopes.
Detecting and Responding to Authentication Attacks
Backend developers rarely stop all attacks entirely, but you can make them visible and limit damage.
What to Log
At minimum, for authentication-related endpoints, log:
- Timestamp.
- Username or account identifier (hashed or partially masked if needed).
- Source IP and user agent.
- Outcome (success or failure).
- Failure reason category (not the exact message given to user):
invalid_credentialsaccount_lockedmfa_requiredmfa_failed
Avoid logging:
- Plaintext passwords.
- Full tokens, session ids, or cookies.
Simple Detection Rules
Examples:
- More than N failed logins for the same account in 5 minutes.
- More than N failed logins from the same IP in 10 minutes.
- Many different usernames failing from one IP in a short period.
- Logins from unusual countries or devices for an account.
Once detected, you can:
- Temporarily block the IP or subnet.
- Trigger CAPTCHA or similar challenges.
- Notify the affected user about suspicious activity.
- Require extra verification on next login.
Summary
Authentication attacks focus on breaking “who are you” rather than directly breaking your encryption or database. As a backend developer, you need to be aware of:
- Credential stuffing where breached credential lists from other sites are tried on your own.
- Brute-force and password spraying that guess passwords for many accounts.
- Phishing which steals correct credentials that your backend will accept.
- Session hijacking and token theft, which bypass the login step completely by reusing valid sessions.
- MFA bypass and other logic flaws that skip critical checks.
- Password reset weaknesses that hand over accounts.
- OAuth and social login mistakes that mis-handle external identities.
- API key leaks that turn into machine account compromise.
Your authentication system is not just a login form. It is the entire set of flows around account access, session management, reset, and external login. Designing and implementing these flows carefully, and watching how they are used in the wild, is one of the most important skills in backend security.
Views: 5
KAHIBARO