在昨天的文章中,我們探討了「授權(Authorization)」問題——也就是使用者進入系統後能做什麼。今天我們要回到更前方的關卡:「認證(Authentication)」——系統如何確定「你真的就是你」?
在 OWASP Top 10 中,身分驗證與認證失敗(Identification and Authentication Failures) 長期霸榜。如果身分驗證防線崩潰,攻擊者不用尋找複雜的程式碼漏洞,只要拿著正確的帳號密碼,就能像合法使用者一樣大搖大擺地登入系統。
想像你家的大門鎖只使用了預設的 0000 或 1234(弱密碼),甚至門口站著一個小偷,正拿著一串包含數百萬把鑰匙的巨大鑰匙圈,一秒鐘試幾十把鎖(暴力破解)。如果你的門鎖沒有「連續錯 5 次就自動鎖住」的機制,門被打開只是時間問題。何況在現實世界中,攻擊者不會一組一組密碼慢慢猜,而是透過自動化腳本與專屬工具進行攻擊。
瞭解攻擊者如何破解密碼,是設計安全認證機制的第一步:
Brute Force Attack(傳統暴力破解攻擊):自動化程式從 a 到 zzzzzz,嘗試所有可能的字元組合。雖然耗時,但若密碼極短,可在幾秒內破解。
Dictionary Attack(字典攻擊):利用事先整理好的「常用密碼清單」(如 123456, password, admin)進行快速比對。大部分使用者設定的弱密碼都會落在字典檔中。
Credential Stuffing(憑證填充 / 撞庫攻擊):攻擊者收集其他網站洩漏的「帳號/密碼」組合,自動套用到各大熱門網站(如 GitHub、AWS、公務系統)嘗試登入。這是因為高達 60% 以上的使用者會在不同網站重複使用相同密碼。
Password Spraying(密碼噴射攻擊):為了避開「單一帳號連續失敗 5 次就鎖定」的防禦機制,攻擊者轉為對大量不同的帳號,只嘗試 1~2 個極常見的密碼(例如 Spring2026! 或 Company2026!)。這樣既能保持低失敗頻率,又能掃出一堆弱密碼帳號。
Rainbow Table Attack(彩虹表攻擊):當資料庫被洩漏時,若密碼雜湊值(Hash)沒有加上 Salt(鹽值),攻擊者可利用雜湊對照表(彩虹表)還原出原始密碼。
情境:登入 API 的驗證機制與限制
無限制且未驗證 MFA
# 完全沒有限制嘗試次數、無防護機制,且僅依靠單一密碼驗證
@app.route('/api/v1/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
user = db.find_user(username)
# 直接比對明文密碼,且登入失敗完全不記錄、不計數
if user and user.password == password:
return jsonify({"status": "success", "token": generate_token(user)})
return jsonify({"status": "failed", "message": "Invalid credentials"}), 401
強認證 + 限流 + MFA 驗證
# 正確:實施速率限制 (Rate Limiting)、密碼雜湊比對與 MFA 驗證
from flask_limiter import Limiter
from werkzeug.security import check_password_hash
# 1. 對登入 API 實施 IP 與帳號等級的連線限流
@limiter.limit("5 per minute")
@app.route('/api/v1/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
mfa_code = request.json.get('mfa_code') # 多因素驗證碼
user = db.find_user(username)
# 2. 檢查帳號是否因多次失敗而被暫時鎖定 (Lockout)
if is_account_locked(username):
return jsonify({"message": "Account temporarily locked. Try again later."}), 429
# 3. 使用安全雜湊(hash)演算法 (如 bcrypt/Argon2) 比對密碼
if not user or not check_password_hash(user.password_hash, password):
record_failed_attempt(username)
return jsonify({"message": "Invalid credentials"}), 401
# 4. 強制第二階段身分驗證 (MFA Check)
if not verify_mfa_totp(user.mfa_secret, mfa_code):
return jsonify({"message": "Invalid MFA Code"}), 401
reset_failed_attempts(username)
return jsonify({"status": "success", "token": generate_token(user)})
要防禦自動化密碼攻擊,必須從政策與技術層面雙管齊下:
自動加鹽(Salting):
當使用者設定密碼(例如 password123)時,系統會在後端隨機產生一串極長的隨機字串(稱為 Salt,鹽值),將「鹽值 + 密碼」組合在一起後,再丟進雜湊函數進行計算。最後把這個「鹽值」與「計算出的 Hash」一起存入資料庫。
適應性耗時 / 工作因子:
傳統的雜湊演算法(如 MD5, SHA-1, SHA-256)設計初衷是為了「極速計算」,這對密碼儲存來說反而成了致命傷(現代顯示卡 GPU 每秒可計算數百億次 SHA-256)。適應性耗時(Work Factor) 允許開發者設定「演算法內部的迴圈迭代次數」或「記憶體消耗量」。計算一次 Hash,故意要求 CPU/GPU 強制執行數萬次的重複計算。