跳至主要内容

A02: 加密失敗(Cryptographic Failures)

以下展示展示如何利用 Hydra 測試暴力破解一個 不安全的登入 API,然後再提供修補方式。


有漏洞的登入服務(允許暴力破解)

這個服務存在以下嚴重漏洞

  1. 使用簡單的 MD5 存儲密碼(密碼雜湊過於脆弱)
  2. 登入 API 沒有任何限制(可無限次嘗試登入)
  3. 沒有 CAPTCHA 或 IP 限制(可以輕易用工具暴力破解)

❌ 漏洞程式碼

package main

import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
)

var users = map[string]string{
"admin": hashPassword("password123"), // ❌ 使用弱密碼
}

// ❌ 使用 MD5 存儲密碼(非常不安全)
func hashPassword(password string) string {
hash := md5.Sum([]byte(password))
return hex.EncodeToString(hash[:])
}

// ❌ 登入 API,沒有防暴力破解保護
func loginHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Invalid form data", http.StatusBadRequest)
return
}

username := r.FormValue("username")
password := r.FormValue("password")

if hashedPwd, exists := users[username]; exists && hashedPwd == hashPassword(password) {
fmt.Fprintf(w, "Welcome, %s!", username)
} else {
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
}
}

func main() {
http.HandleFunc("/login", loginHandler)

fmt.Println("Server running on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

利用 Hydra 進行暴力破解

Hydra 是一款專門用來測試弱密碼的暴力破解工具,可以針對 Web 應用、SSH、FTP 等多種協議進行密碼猜測。

🔍 使用 Hydra 來破解密碼

假設我們的目標 API 是:

http://localhost:8080/login

使用 Hydra 進行暴力破解,簡單創立一個密碼字典 password.txt,我們就可以發現他被破解出來了:

hydra -l admin -P password.txt 127.0.0.1 -s 8080 http-post-form "/login:username=^USER^&password=^PASS^:1=:F=Invalid credentials"

說明:

  • -l admin:指定帳號為 admin
  • -P password.txt:使用 password.txt 進行密碼暴力破解。
  • 127.0.0.1:目標主機為本機 IP。
  • -s 8080:指定使用的端口為 8080。
  • http-post-form:使用 HTTP POST 表單方式。
  • "/login:username=^USER^&password=^PASS^:1=:F=Invalid credentials"
    • /login:登入頁面路徑。
    • username=^USER^&password=^PASS^:表單數據,替換用戶名與密碼。
    • 1=:檢查是否有錯誤回應。
    • F=Invalid credentials:伺服器回應 "Invalid credentials" 表示密碼錯誤。

🔍 破解成功範例

Hydra 會告訴我們,他破解的密碼為password123

Hydra v9.6dev (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-02-12 23:01:10
[DATA] max 2 tasks per 1 server, overall 2 tasks, 2 login tries (l:1/p:2), ~1 try per task
[DATA] attacking http-post-form://127.0.0.1:8080/login:username=^USER^&password=^PASS^:1=:F=Invalid credentials
[8080][http-post-form] host: 127.0.0.1 login: admin password: password123
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-02-12 23:01:11

這代表我們成功暴力破解了 admin 帳號的密碼!💀


修補方式

✅ 修正方式 1:使用 bcrypt 儲存密碼

取代 MD5,改用 bcrypt(防止密碼彩虹表攻擊):

import "golang.org/x/crypto/bcrypt"

func hashPassword(password string) (string, error) {
hashedPwd, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPwd), nil
}

✅ 修正方式 2:加入失敗次數限制

防止 Hydra 無限次暴力破解:

var loginAttempts = make(map[string]int)

func loginHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Invalid form data", http.StatusBadRequest)
return
}

username := r.FormValue("username")
password := r.FormValue("password")

// ✅ 限制最多 5 次嘗試
if loginAttempts[username] >= 5 {
http.Error(w, "Too many failed attempts, please try again later.", http.StatusTooManyRequests)
return
}

if hashedPwd, exists := users[username]; exists && hashedPwd == hashPassword(password) {
fmt.Fprintf(w, "Welcome, %s!", username)
loginAttempts[username] = 0 // 重置失敗次數
return
}

// 失敗次數增加
loginAttempts[username]++
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
}

✅ 修正方式 3:加入 CAPTCHA

每次登入錯誤 3 次後,要求用戶輸入 CAPTCHA,阻止自動化攻擊。


修補後的安全性提升

修補項目具體改善
使用 bcrypt防止彩虹表攻擊,讓密碼更難破解
失敗次數限制阻止暴力破解(Hydra 失效)
CAPTCHA防止自動化工具攻擊
HTTPS避免密碼在傳輸時被攔截