方案選型對比
| 方案 | 優點 | 缺點 | 適用場景 |
| 文本文件 (明文/簡單加密) | 零依賴、易調試、移植性強 | 併發寫入難、查詢慢、安全性弱 | 單用戶桌面工具、教學演示、嵌入式設備 |
| SQLite (嵌入式數據庫) | SQL 查詢、事務、併發、單文件 | 需鏈接庫、稍大 | 本地應用、移動端、中小型項目 |
| MySQL/PostgreSQL 客戶端庫 | 企業級特性、網絡透明 | 需獨立服務進程、部署複雜 | C/S 架構、多用戶並發、生產系統 |
方案 A:文本文件 + 簡單加密 (教學級)
數據結構
typedef struct {
char username[32];
char password_hash[65]; // SHA-256 hex 64 字符 + \0
char salt[17]; // 16 字節隨機鹽 + \0
} UserRecord;
核心函數
// 註冊
bool registerUser(const char *username, const char *password) {
// 1. 檢查用戶名是否存在
// 2. 生成隨機鹽
// 3. 計算 hash = SHA256(salt + password)
// 4. 追加寫入文件
FILE *fp = fopen("users.dat", "ab");
if (!fp) return false;
UserRecord rec;
strncpy(rec.username, username, 31);
// ... 填充 salt, password_hash
fwrite(&rec, sizeof(UserRecord), 1, fp);
fclose(fp);
return true;
}
// 登錄驗證
bool loginUser(const char *username, const char *password) {
FILE *fp = fopen("users.dat", "rb");
if (!fp) return false;
UserRecord rec;
while (fread(&rec, sizeof(UserRecord), 1, fp) == 1) {
if (strcmp(rec.username, username) == 0) {
// 計算 hash = SHA256(rec.salt + password)
// 比較 hash == rec.password_hash
fclose(fp);
return true; // 簡化
}
}
fclose(fp);
return false;
}
加密關鍵點
- 不可存明文密碼:必須存鹽值哈希。
- 使用成熟哈希:SHA-256 (需外部庫如 OpenSSL/mbedTLS) 或 PBKDF2/bcrypt/argon2 (更安全)。
- 防時序攻擊:比較哈希用恒定時間比較函數。
方案 B:SQLite 3 嵌入式數據庫 (推薦生產級)
優勢
- 標準 SQL,支持索引、事務、併發 (WAL 模式)。
- 單文件
users.db,無需服務進程。
- C API 成熟穩定,
sqlite3.h + sqlite3.c 直接編入項目。
關鍵代碼片段
sqlite3 *db;
sqlite3_open("users.db", &db);
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"username TEXT UNIQUE NOT NULL,"
"password_hash TEXT NOT NULL,"
"salt TEXT NOT NULL"
")", 0, 0, 0);
// 註冊 (參數化查詢防注入)
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, "INSERT INTO users (username, password_hash, salt) VALUES (?, ?, ?)", -1, &stmt, 0);
sqlite3_bind_text(stmt, 1, username, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, hash, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, salt, -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// 登錄
sqlite3_prepare_v2(db, "SELECT password_hash, salt FROM users WHERE username = ?", -1, &stmt, 0);
sqlite3_bind_text(stmt, 1, username, -1, SQLITE_TRANSIENT);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *db_hash = (const char*)sqlite3_column_text(stmt, 0);
const char *db_salt = (const char*)sqlite3_column_text(stmt, 1);
// 驗證...
}
sqlite3_finalize(stmt);
sqlite3_close(db);
安全最佳實踐清單
- 密碼最小長度 8 位,強制大小寫數字特殊字符混合。
- 使用 **Argon2id** (推薦) 或 **bcrypt** / **PBKDF2-HMAC-SHA256** (迭代 ≥ 100,000)。
- 登錄失敗限制嘗試次數 + 鎖定時間 (防暴力破解)。
- 敏感操作記錄審計日志 (用戶、時間、IP、操作)。
- 文件/數據庫權限設置為僅擁有者讀寫 (chmod 600)。
總結建議
- **教學/玩具項目**:文本文件 + SHA-256 即可,重點理解「鹽值哈希」概念。
- **實際產品/課程設計**:**直接用 SQLite**,代碼量差異不大,但健壯性、可擴展性、面試加分項質變。
- **切勿自行設計加密算法**,務必調用成熟密碼學庫 (OpenSSL, libsodium, mbedTLS)。