# A complete conquest of authentication vulnerabilities [2026]: username enumeration, brute force, 2FA bypass, password reset — a version faithful to the official docs

> An in-depth look at vulnerabilities and attack techniques in authentication (login) mechanisms, faithful to the PortSwigger Web Security Academy. Username enumeration (message differences, response-time differences), brute force and bypassing rate limits, account-lockout loopholes, multi-factor authentication (2FA/MFA) bypass, password-reset poisoning, Remember Me / password-change flaws, and root-cause defenses via rate limiting, constant responses, MFA, and safe reset design — explained strictly within your own lab.

- Published: 2026-06-28
- Author: 友田 陽大
- Tags: セキュリティ, ホワイトハッカー, 認証, 脆弱性診断, Webセキュリティ
- URL: https://tomodahinata.com/en/blog/authentication-vulnerabilities-brute-force-2fa-bypass-attack-guide
- Category: 実践Webハッキング技法
- Pillar guide: https://tomodahinata.com/en/blog/web-application-hacking-techniques-methodology-owasp-portswigger-guide

## Key points

- Authentication holes are born where things are 'guessable, enumerable.' Username enumeration works from differences in error messages, response times, and HTTP status. A two-stage setup: first identify valid usernames, then brute-force the password.
- Flaws in brute-force protection are targeted: IP-based rate limits are bypassed by IP rotation or X-Forwarded-For, and account lockout is bypassed by 'password spraying' that spreads across users. The lockout itself also becomes a username-enumeration signal.
- Multi-factor authentication (2FA/MFA) bypass: verification-logic flaws (you can transition skipping the second factor), OTP brute force (no rate/attempt limits), and verifying the wrong user. MFA works only 'when implemented correctly.'
- Password reset is authentication's back door. Guessable tokens, sending a reset link to the attacker's domain via Host-header poisoning, and swapping the user identifier let you take over someone else's password.
- Root-cause defenses: close username enumeration with constant error responses and response times, robust rate limiting and MFA, unguessable, short-lived, single-use, user-bound reset tokens, and trusting the server URL rather than the Host header.

---

Authentication is the **front line** of an app's security. If it's breached, the authorization and cryptography beyond it become meaningless. As [PortSwigger](https://portswigger.net/web-security/authentication) says, "authentication vulnerabilities allow attackers access to sensitive data," and if a privileged account is taken, the whole app is hijacked. This article explains those attack techniques faithfully to the official source.

> **An absolute premise:** brute force, enumeration, and reset poisoning all carry **strong invasiveness**. Execute only within a [legal lab](/blog/ethical-hacking-home-lab-kali-juice-shop-ctf-self-study-roadmap-guide) or a scope authorized in writing. Unauthorized login attempts are themselves an act of unauthorized access (→ [the legal guide](/blog/ethical-hacker-law-japan-unauthorized-access-act-active-cyber-defense-disclosure-guide)). The map is the [pillar](/blog/web-application-hacking-techniques-methodology-owasp-portswigger-guide).

---

## 1. Username enumeration — the first stage of the attack

Before brute force, **confirming "valid usernames that exist"** makes efficiency jump. Apps often leak the existence of a user through **slight differences**.

| Leak signal | Example |
|---|---|
| **Message difference** | "Invalid username" vs "Invalid password" |
| **Response-time difference** | Only existing users trigger a hash check, so the response is slower |
| **HTTP status / subtle wording difference** | Presence or absence of a trailing period, a different redirect target |

```http
# Burp Intruder で username を辞書にして送り、レスポンスの差分（長さ・文言・時間）を観察
POST /login HTTP/1.1
Host: lab.example
Content-Type: application/x-www-form-urlencoded

username=§candidate§&password=wrongpass
```

With a **Sniper** attack in [Burp Intruder](/blog/burp-suite-getting-started-proxy-repeater-intruder-web-security-testing-guide), cycle only the `username` position and pin down valid users from **outliers in response length or response time**.

---

## 2. Brute force and bypassing protections

Once a valid username is known, you brute-force the password. The problem is **flaws in the protection mechanisms**.

### 2.1 Bypassing IP-based rate limits

"Block after a certain number of failures from the same IP" can be bypassed by header spoofing or IP rotation.

```http
# X-Forwarded-For を毎回変えて、IPベースのカウンタを欺く（脆弱な実装で有効）
X-Forwarded-For: 1.2.3.4
```

### 2.2 The account-lockout loophole — password spraying

"Lock after N failures for one user" is bypassed by **password spraying** — trying the same weak password once each against many users. Each user's failure count never reaches the lockout threshold.

```text
# 縦に弱いパスワードを固定し、横に大量ユーザーを試す（Pitchfork/Cluster bomb的）
user001 : Password1!
user002 : Password1!
...                      ← どのユーザーもロックされない
```

Furthermore, **the "you've been locked out" response itself** becomes an enumeration signal that "this user exists."

---

## 3. Multi-factor authentication (2FA/MFA) bypass

MFA is powerful, but it works only **when implemented correctly**. Typical holes PortSwigger lists:

### 3.1 Missing verification logic (skipping the second stage)

When the session issued after the first stage (password) succeeds **can reach protected resources even with the second stage incomplete**. Skip the 2FA input screen and hit the destination URL directly, and it goes through.

```http
# 2FAページを経由せず、ログイン後のページへ直接アクセスして通るか確認
GET /my-account HTTP/1.1
Host: lab.example
Cookie: session=<1段目だけ通したセッション>
```

### 3.2 OTP brute force

If the verification code (OTP) has **no attempt limit or rate limit**, you can brute-force the 4–6 digits. It can be broken just by cycling `0000`–`9999` in [Burp Intruder](/blog/burp-suite-getting-started-proxy-repeater-intruder-web-security-testing-guide).

### 3.3 Verifying the wrong user

An implementation mistake where the second-stage verification **accepts a code for a different user than the first stage's user**. It happens when account identification is left to the client.

---

## 4. Other mechanisms — reset, Remember Me, change

### 4.1 Password-reset poisoning (the back door)

Password reset "temporarily relaxes identity verification," so it easily becomes authentication's **back door**.

- **Guessable tokens**: sequential, timestamp, or short tokens are predictable.
- **Host-header poisoning**: if the reset-link generation uses the **`Host` header**, an attacker can send `Host: evil.example` and **plant a link to the attacker's domain in the email sent to the victim**. When the victim clicks, the token goes to the attacker.
- **Identifier swapping**: if the `username`/`userId` in the reset-confirm request can be changed to someone else, you can set another person's password.

```http
# Hostヘッダ汚染：被害者宛リセットメールのリンクが攻撃者ドメインを指すよう仕込む
POST /forgot-password HTTP/1.1
Host: evil-attacker.example          ← サーバーがこれを信頼してリンク生成すると致命的
Content-Type: application/x-www-form-urlencoded

username=victim
```

### 4.2 Remember Me / password change

If the "keep me logged in" token is **guessable** (a hash of the username, etc.), it's forged. If a password change **doesn't verify the current password**, it's used for persistence after session hijacking.

---

## 5. [Defender side] Root-cause defenses

Once you understand the attack surface, close it by design. The essence of PortSwigger and the [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html):

```ts
// ✅ ユーザー名列挙を防ぐ：成否で「同一の」メッセージ・同等の処理時間を返す
async function login(username: string, password: string) {
  const user = await findUser(username);
  // ユーザーが存在しなくても必ずハッシュ照合を走らせ、応答時間を一定化（タイミング差を消す）
  const hash = user?.passwordHash ?? DUMMY_HASH;
  const ok = await verifyPassword(password, hash); // Argon2id 等
  if (!user || !ok) {
    // 列挙を許さない一定の文言（どちらが違うかを明かさない）
    throw new AuthError("ユーザー名またはパスワードが正しくありません");
  }
  // ...
}
```

Design principles:

- **Close enumeration**: return the **same message, equivalent response time, and same status** for success and failure.
- **Robust rate limiting**: **per-account + global**, not just per-IP. Don't trust `X-Forwarded-For`. Correct rate limiting on serverless is in the [dedicated guide](/blog/nextjs-serverless-rate-limiting-vercel-guide).
- **Implement MFA correctly**: don't let it reach protected resources until the second stage is complete. Put an **attempt limit** on the OTP. The code should be short-lived, single-use, and **bound to the first-stage user**.
- **Make password reset safe**: tokens that are **unguessable (CSPRNG), long, short-lived, single-use, and user-bound**. For link generation, **don't use the `Host` header; trust the server-side canonical URL** (a defense against [HTTP Host header attacks](https://portswigger.net/web-security/host-header)).
- **Password strength and hashing**: store with Argon2id, etc., and reject weak passwords. Details in the [password-hashing/cryptography guide](/blog/password-hashing-argon2-encryption-key-management-applied-cryptography-guide).
- **Sessions**: regenerate the session ID after authentication, and add `HttpOnly`/`Secure`/`SameSite`.

For the choice of leaning the authentication foundation itself toward managed (Cognito/Auth0/Clerk/Supabase Auth), see [authentication-platform selection](/blog/auth-platform-selection-2026-cognito-auth0-clerk-supabase). **"Don't implement authentication yourself"** is the safest call in many projects.

---

## 6. Summary

- **A two-stage enumerate→brute-force setup**: first identify valid usernames from message and time differences.
- **Protection mechanisms get bypassed**: IP limits by header spoofing, lockout by password spraying.
- **MFA depends on implementation**: watch for second-stage skip, OTP brute force, and verifying the wrong user.
- **Reset is the back door**: guessable tokens, Host-header poisoning, identifier swapping.
- **Root-cause defenses**: close enumeration with constant responses + robust rate limiting + correct MFA + safe reset + strong hashing.

Next, head to [the complete conquest of SSTI](/blog/server-side-template-injection-ssti-rce-detection-exploitation-guide), which hijacks the template engine to reach RCE.

---

### References (official primary sources)

- [PortSwigger: Authentication](https://portswigger.net/web-security/authentication) / [2FA](https://portswigger.net/web-security/authentication/multi-factor) / [HTTP Host header attacks](https://portswigger.net/web-security/host-header)
- [OWASP: Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) / [Forgot Password Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html)
- [NIST SP 800-63B (Digital Identity Guidelines)](https://pages.nist.gov/800-63-3/sp800-63b.html)
