防暴力破解與帳號保護
帳號密碼系統不能只靠「密碼正確才登入」。攻擊者不一定會手動猜密碼,而是會用外洩帳密、代理 IP、自動化工具、低速嘗試、針對高價值帳號的社交工程,慢慢測試你的登入與帳號恢復流程。
一句話回答:防暴力破解與帳號保護要用分層策略:登入、OTP、忘記密碼等認證端點都要做 rate limit 與 throttling,限制維度包含 IP、帳號、IP + 帳號、裝置、租戶與風險分數;account lockout 要避免被濫用成 DoS,CAPTCHA 只能作為輔助;credential stuffing 要靠外洩密碼檢查、MFA、異常登入偵測、裝置通知、session 撤銷與監控告警,而不是只靠單一鎖帳號規則。
一、 必考觀念
1. 攻擊不只有 brute force
常見攻擊類型:
| 類型 | 說明 | 特徵 |
|---|---|---|
| Brute force | 猜單一帳號的密碼 | 同帳號多次失敗 |
| Password spraying | 少量常見密碼打很多帳號 | 每個帳號失敗不多,但範圍大 |
| Credential stuffing | 拿外洩帳密到你的站測試 | 成功率可能不低,分散式來源 |
| Account enumeration | 測試哪些帳號存在 | 註冊、登入、忘記密碼訊息差異 |
| OTP brute force | 猜驗證碼 | 對短碼重複嘗試 |
| Recovery abuse | 攻擊忘記密碼或恢復流程 | reset email / SMS 被濫用 |
如果只針對「同一帳號錯 5 次就鎖」,會擋不住很多低速、分散式或橫向攻擊。
2. 防護要放在所有認證端點
需要保護的端點不只有 /login:
POST /login
POST /mfa/verify
POST /password-reset-requests
POST /password-resets
POST /email-verifications
POST /phone-verifications
POST /sessions/refresh
POST /register攻擊者會找最弱的一條路。
例如登入 API 很安全,但忘記密碼可以無限制寄信,仍然會造成成本攻擊與帳號枚舉。
3. 安全控制要避免傷害正常使用者
防護不是越嚴越好。
太嚴會造成:
- 使用者輸錯幾次就被鎖。
- 攻擊者可故意鎖別人帳號。
- 客服量暴增。
- 高價值使用者無法登入。
- CAPTCHA 讓可用性下降。
所以好的帳號保護是「分層、漸進、可觀測」,不是單純粗暴封鎖。
二、 Rate Limit 與 Throttling
1. Rate limit 是第一道防線
Rate limit 目標:
- 降低暴力猜測速度。
- 降低 credential stuffing 吞吐量。
- 保護 Email / SMS 成本。
- 防止 API 被打爆。
- 讓異常行為更容易被觀測。
NIST SP 800-63B 要求 verifier 對單一 subscriber account 的失敗驗證嘗試實作有效 rate limiting;OWASP Authentication Cheat Sheet 也把 login throttling 列為防止猜密碼的重要控制。
2. 限制維度
不要只用 IP。
| 維度 | 能防什麼 | 限制 |
|---|---|---|
| IP | 單來源攻擊 | 代理、NAT、公司網路會誤傷 |
| 帳號 identifier | 針對單帳號猜密碼 | 可能被 DoS 鎖帳號 |
| IP + identifier | 更精準 | 分散式攻擊仍可繞 |
| 裝置指紋 | 輔助判斷 | 隱私與可靠性要小心 |
| User account | 登入後敏感操作限制 | 需要知道帳號 |
| Tenant / org | 多租戶保護 | 大客戶可能尖峰流量高 |
| ASN / 國家 / 區域 | 風險偵測 | 易誤判,不能單獨決策 |
實務上通常多個維度一起用。
3. 漸進式延遲
比起立即鎖帳,可以先做:
失敗 1-3 次:正常回應
失敗 4-5 次:增加短延遲
失敗 6-10 次:要求等待或 CAPTCHA
失敗更多:暫時限制該帳號或來源概念:
function getLoginDelay(failedAttempts: number) {
if (failedAttempts < 4) {
return 0
}
return Math.min(2 ** (failedAttempts - 4), 300)
}延遲最好在 server side 強制執行,不能只靠前端倒數。
4. 回應要清楚但不洩漏資訊
可以告訴使用者:
嘗試次數過多,請 5 分鐘後再試。但不要告訴攻擊者:
這個帳號存在,而且目前還剩 1 次可以猜。NIST 也提醒 throttling 時應告知使用者要等待多久,避免正常使用者困惑;但資訊揭露仍要控制。
三、 Account Lockout
1. 鎖帳號可以防猜密碼,也可能被濫用
簡單策略:
同一帳號連續錯 5 次 -> 鎖 15 分鐘問題是攻擊者可以:
拿別人的 email 故意輸錯 5 次
-> 讓真正使用者無法登入這是一種 denial-of-service。
2. 比永久鎖定更好的策略
可以考慮:
- 漸進式延遲。
- 短時間 soft lock。
- 只限制可疑來源,不限制已信任裝置。
- 要求 MFA 或 email 確認。
- 高風險時才要求 CAPTCHA。
- 通知使用者可疑嘗試。
- 提供安全的解鎖流程。
例如:
新裝置 + 多次失敗 -> 限制該來源
已信任裝置 + 使用者偶爾輸錯 -> 不粗暴鎖死3. 成功登入後要重置失敗計數
成功登入後通常應該:
- 清除或降低 failed attempts。
- 更新 lastLoginAt。
- 寫 login_success。
- 保留歷史失敗事件供偵測。
但不要把失敗紀錄完全刪到不可追蹤,否則會失去攻擊線索。
四、 CAPTCHA 的位置
1. CAPTCHA 是輔助,不是核心安全
CAPTCHA 可以增加自動化成本,但不能取代:
- 密碼 hash。
- rate limit。
- MFA。
- 異常登入偵測。
- 外洩密碼檢查。
攻擊者可能使用:
- CAPTCHA solving service。
- 真實瀏覽器自動化。
- 低速攻擊。
- 已知帳密直接登入。
所以 CAPTCHA 不應該是唯一防線。
2. 何時出現 CAPTCHA?
比較好的方式是風險式觸發:
| 情況 | 是否觸發 |
|---|---|
| 第一次正常登入 | 不需要 |
| 同 IP 大量失敗 | 可以 |
| 同帳號多次失敗 | 可以 |
| 代理 / Tor / 高風險 ASN | 視情況 |
| 註冊大量帳號 | 可以 |
| 忘記密碼大量請求 | 可以 |
不要一進登入頁就讓所有使用者過 CAPTCHA,體驗會很差。
3. 無障礙與替代流程
CAPTCHA 可能影響:
- 視覺障礙使用者。
- 行動裝置使用者。
- 公司網路或 VPN 使用者。
- 隱私工具使用者。
如果產品很重要,應提供替代流程,例如:
- Email 驗證。
- MFA。
- 客服協助。
- 已信任裝置通過。
五、 Credential Stuffing
1. Credential stuffing 是什麼?
Credential stuffing 不是猜密碼,而是拿其他網站外洩的帳密組合來試:
user@example.com / leakedPassword123因為很多人重用密碼,所以成功率可能不低。
OWASP Credential Stuffing Prevention Cheat Sheet 也強調,這類攻擊不是暴力猜測,而是自動化測試已知帳密組合。
2. 為什麼一般 lockout 擋不住?
Credential stuffing 常見特徵:
- 分散 IP。
- 每個帳號嘗試次數少。
- 嘗試帳號數很多。
- 使用真實外洩密碼。
- 成功後低調操作。
如果規則是「同一帳號錯 5 次才鎖」,可能每個帳號只試 1 次就繞過。
3. 防護方式
| 手段 | 作用 |
|---|---|
| MFA | 最有效降低密碼重用風險 |
| 外洩密碼檢查 | 禁止使用已知外洩密碼 |
| 風險式驗證 | 可疑登入要求額外驗證 |
| 異常登入偵測 | 找到大量低速嘗試 |
| 裝置通知 | 讓使用者知道新裝置登入 |
| Session 管理 | 可疑時撤銷 session |
| IP / ASN reputation | 輔助判斷來源風險 |
| Bot detection | 偵測自動化行為 |
OWASP Credential Stuffing Prevention Cheat Sheet 也指出,真正的 MFA 是抵禦 credential stuffing 的最佳防線之一。
4. 外洩密碼檢查
建立或修改密碼時,應檢查:
- 常見密碼。
- 已外洩密碼。
- 與帳號資訊相似的密碼。
NIST SP 800-63B 建議在使用者建立或更改密碼時,將候選密碼與常見、預期或已外洩的密碼清單比對。
這不是登入當下唯一防線,但可以降低未來被 credential stuffing 成功的機率。
延伸讀:密碼如何安全儲存。
六、 防帳號枚舉
1. 哪些地方會枚舉帳號?
常見端點:
| 端點 | 危險回應 |
|---|---|
| login | 此 email 不存在 |
| register | 此 email 已註冊 |
| password reset | 找不到帳號 |
| email verification resend | 此 email 尚未註冊 |
| MFA challenge | 此帳號未啟用 MFA |
攻擊者可以用這些差異建立有效帳號清單,再進行釣魚、撞庫或社交工程。
2. 通用回應
登入:
帳號或密碼錯誤。忘記密碼:
如果這個信箱已註冊,我們會寄出重設指示。重寄驗證信:
如果資料正確,我們會寄出驗證訊息。外部通用,內部 log 詳細。
3. 時間差異也可能洩漏
如果帳號不存在時立刻回應,帳號存在時跑 password hash 才回應,攻擊者可能用時間差推測帳號。
登入流程可以用 dummy hash 降低差異:
const user = await userRepository.findByIdentifier(identifier)
const passwordHash = user?.passwordHash ?? dummyPasswordHash
const valid = await passwordHasher.verify(password, passwordHash)
if (!user || !valid) {
throw new InvalidCredentialsError()
}延伸讀:帳號密碼登入流程。
七、 異常登入偵測
1. 成功登入也要觀察
風險不只在失敗登入。
成功登入也可能可疑:
- 新裝置。
- 新地點。
- Impossible travel。
- 大量失敗後突然成功。
- 深夜登入管理員帳號。
- 登入後立刻改 email / password。
- 登入後批量匯出資料。
這些情境可以觸發:
- 安全通知。
- MFA step-up。
- 限制敏感操作。
- session 標記為高風險。
- SOC / 管理員告警。
2. 風險分數
可以建立簡化風險分數:
type LoginRisk = {
newDevice: boolean
newCountry: boolean
failedAttemptsRecently: number
ipReputation: 'low' | 'medium' | 'high'
accountRole: 'user' | 'admin'
}決策:
低風險 -> 正常登入
中風險 -> 通知使用者或要求 MFA
高風險 -> 阻擋、要求更強驗證或人工處理3. 不要過度收集
裝置指紋與地理位置牽涉隱私。
應注意:
- 只收必要資料。
- 告知使用者安全用途。
- 設定保存期限。
- 避免把完整個資寫入 log。
- 遵守產品所在地法規。
帳號保護不能變成無限制監控。
八、 通知、稽核與告警
1. 使用者通知
適合通知的事件:
- 新裝置登入。
- 密碼被修改。
- email / phone 被修改。
- MFA 被關閉。
- 高風險登入被阻擋。
- 多次失敗登入。
通知內容應包含:
時間
大略位置
裝置或瀏覽器
如果不是本人,下一步怎麼做不要在通知裡放敏感 token。
2. Audit log
建議記錄:
| 事件 | 欄位 |
|---|---|
| login_failed | identifierHash、ip、reason |
| login_success | userId、sessionId、ip |
| rate_limited | rule、key、retryAfter |
| account_locked | userId、reason、expiresAt |
| mfa_failed | userId、method、ip |
| password_reset_requested | identifierHash、ip |
| credential_stuffing_suspected | tenant、pattern、count |
這些資料能支撐客服、安全調查與風控調整。
3. 告警要聚合
不要每一次登入失敗都通知工程師。
更好的告警條件:
- 某租戶登入失敗率異常升高。
- 某 IP / ASN 攻擊大量帳號。
- 管理員帳號連續失敗。
- password reset 請求突然暴增。
- MFA 失敗率異常。
- credential stuffing 成功登入數上升。
告警要有門檻、聚合與去重,否則團隊會麻木。
九、 追問題庫
Q1:如何防止暴力破解登入?
使用 rate limit、login throttling、失敗次數限制、漸進式延遲、風險式 CAPTCHA、MFA、外洩密碼檢查、異常登入偵測與告警。不要只靠單一帳號鎖定。
Q2:Account lockout 有什麼問題?
攻擊者可以故意對某個帳號輸錯多次,讓真正使用者無法登入,形成 DoS。比較好的方式是短時間 soft lock、漸進式延遲、限制可疑來源,或要求額外驗證。
Q3:CAPTCHA 可以防暴力破解嗎?
只能輔助。CAPTCHA 可以提高自動化成本,但會被代解服務、真實瀏覽器自動化或低速攻擊繞過。核心仍是 rate limit、MFA、風險偵測與密碼政策。
Q4:Credential stuffing 和 brute force 差在哪?
Brute force 是猜密碼;credential stuffing 是拿其他地方外洩的帳密組合來試。後者不需要大量猜同一帳號,因此傳統 lockout 不一定有效。
Q5:如何避免帳號枚舉?
登入、忘記密碼、註冊、重寄驗證信等流程對外使用通用訊息,避免回覆 email 是否存在。內部 log 可以記詳細原因,但外部不要暴露。
Q6:失敗登入要不要通知使用者?
視情況。偶發輸錯不一定通知;若出現新地點、多次失敗、高價值帳號或攻擊特徵,可以通知使用者或要求額外驗證。
十、 實作題
題目:設計 Login Rate Limiter
需求:
- 同 IP 每分鐘最多 N 次。
- 同 identifier 每 15 分鐘最多 N 次失敗。
- IP + identifier 超過門檻時增加延遲。
- 管理員帳號更嚴格。
- 對外不暴露帳號是否存在。
可以這樣設計 key:
type LoginLimitKeys = {
ipKey: string
identifierKey: string
pairKey: string
}
function createLoginLimitKeys(input: {
ip: string
identifier: string
}) {
const identifierHash = hashIdentifier(normalizeIdentifier(input.identifier))
return {
ipKey: `login:ip:${input.ip}`,
identifierKey: `login:identifier:${identifierHash}`,
pairKey: `login:pair:${input.ip}:${identifierHash}`,
}
}登入時:
async function checkLoginLimit(input: LoginAttempt) {
const keys = createLoginLimitKeys(input)
const [ipCount, identifierCount, pairCount] = await Promise.all([
limiter.count(keys.ipKey, { window: '1m' }),
limiter.count(keys.identifierKey, { window: '15m' }),
limiter.count(keys.pairKey, { window: '15m' }),
])
if (ipCount > 60 || identifierCount > 10 || pairCount > 5) {
await auditLog.write({
type: 'rate_limited',
identifierHash: hashIdentifier(input.identifier),
ip: input.ip,
})
throw new TooManyAttemptsError()
}
}密碼驗證失敗後:
await limiter.increment(keys.identifierKey)
await limiter.increment(keys.pairKey)成功登入後:
await limiter.reset(keys.identifierKey)
await limiter.reset(keys.pairKey)這只是簡化版本。真實系統還要考慮 Redis 原子性、滑動窗口、分散式部署、NAT 誤傷與高風險帳號策略。
題目延伸:偵測 Credential Stuffing
可以觀察:
大量不同帳號
相似 user agent
代理 IP 分散
失敗率高但每帳號嘗試次數低
短時間成功登入後出現高風險操作處理:
提高風險分數
要求 MFA
阻擋高風險來源
通知使用者
暫時限制敏感操作十一、 資深視角
1. 帳號保護是風控系統,不是單一規則
成熟系統會把登入看成風險事件:
帳號
密碼
裝置
IP
地點
歷史行為
角色
操作風險然後根據風險決定:
- 放行。
- 延遲。
- CAPTCHA。
- MFA。
- 阻擋。
- 通知。
- 人工審核。
2. 管理員帳號要更嚴格
後台、財務、客服、醫療、政府管理員帳號通常要:
- 強制 MFA。
- 限制 IP 或 VPN。
- 更嚴格的異常登入告警。
- 操作 audit log。
- 高風險操作重新驗證。
- 更短 session。
不要用一般會員規則保護高權限帳號。
3. 指標比直覺可靠
要看:
- 登入成功率。
- 登入失敗率。
- rate limited 次數。
- CAPTCHA 通過率。
- lockout 次數。
- password reset 請求量。
- MFA challenge 成功率。
- credential stuffing 偵測事件。
沒有指標,就很難知道規則是擋住攻擊,還是在傷害正常使用者。
總結
| 問題 | 回答重點 |
|---|---|
| 暴力破解 | rate limit、throttling、漸進延遲、MFA |
| Account lockout | 可防猜密碼,但可能被濫用成 DoS |
| CAPTCHA | 輔助控制,不是核心安全 |
| Credential stuffing | 外洩帳密重放,需要 MFA、風險偵測與外洩密碼檢查 |
| 帳號枚舉 | 通用回應、內外訊息分離、降低時間差 |
| 異常登入 | 新裝置、新地點、大量失敗後成功要提高風險 |
| 告警 | 聚合、去重、看趨勢,不要每次失敗都吵 |
| 高權限帳號 | 強制 MFA、IP 限制、稽核與更嚴格策略 |
一句完整的面試回答可以是:
防暴力破解不能只靠「錯 5 次鎖帳號」。我會先對所有認證端點做 rate limit,包括登入、MFA、OTP、忘記密碼與重寄驗證信;限制維度不只 IP,還包含 identifier、IP + identifier、裝置、租戶與帳號風險。對多次失敗可以採用漸進式延遲、短時間 soft lock 或風險式 CAPTCHA,但要避免攻擊者故意鎖別人帳號。Credential stuffing 要另外處理,因為它是用外洩帳密低速分散測試,不能只靠單帳號 lockout;更有效的是 MFA、外洩密碼檢查、異常登入偵測、新裝置通知與 session 撤銷。登入、忘記密碼、註冊流程對外要用通用訊息避免帳號枚舉,內部則用 audit log 記詳細原因。高權限帳號要有更嚴格策略,例如強制 MFA、IP allowlist、短 session 與告警。