為了在資料庫中安全地儲存恢復碼(Recovery Codes),這些恢復碼用於當使用者無法使用常規的二步驟驗證方法時仍能存取其帳戶,可以按照以下步驟設計資料庫架構:
文章目錄
設計思路
獨立的表格:為了安全性和靈活性,恢復碼應該儲存在使用者表格之外的一個獨立表格中。 這樣做可以確保即使用戶表被訪問,恢復碼也能保持安全。
哈希(Hash)存儲:出於安全考慮,恢復碼應該以哈希形式存儲,類似於密碼的存儲方式。 這樣,即使資料庫洩露,恢復碼也不會立即暴露。
關聯使用者ID:每個恢復碼都應該關聯到一個特定的使用者ID,以便在驗證恢復碼時能夠識別使用者。
資料庫結構定義
恢復碼資料庫表格設計:
1 2 3 4 5 6 7 8 |
CREATE TABLE recovery_codes ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, code_hash VARCHAR(255) NOT NULL, used TINYINT(1) DEFAULT 0, -- 0 for unused, 1 for used created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); |
在這個設計中:
id 是恢復碼的唯一識別碼。
user_id 是恢復碼所屬的使用者的ID,它透過外鍵與使用者表格的ID關聯。
code_hash 是恢復碼的雜湊值。
used 標記恢復碼是否已被使用,以防止同一個恢復碼重複使用。
created_at 記錄了恢復碼的建立時間,有助於管理和定期更新恢復碼。
處理恢復碼
產生和儲存:當產生新的恢復碼時,先對其進行雜湊處理,然後將雜湊值儲存到資料庫中。
驗證:當使用者嘗試使用恢復碼登入時,將提交的恢復碼進行相同的雜湊處理,然後與資料庫中的雜湊值進行比較。
標記為已使用:一旦一個恢復碼被成功驗證,應該立即將其標記為已使用,以防止再次使用。
Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?php if (!defined('BASEPATH')) exit ('No direct script access allowed'); class Recovery_code_model extends CI_Model { var $id = NULL; var $user_id = NULL; var $code_hash = NULL; var $used = 0; public function __construct() { parent::__construct(); } // 插入新的恢復碼 public function insert_recovery_code($user_id, $recovery_code) { $code_hash = password_hash($recovery_code, PASSWORD_DEFAULT); $data = array( 'user_id' => $user_id, 'code_hash' => $code_hash, 'used' => 0, // 初始狀態未使用 ); return $this->db->insert('recovery_codes', $data); } // 標記恢復碼為已使用 public function verify_recovery_code($user_id, $input_code) { $this->db->where('user_id', $user_id); $this->db->where('used', 0); // 僅考慮未使用的恢復碼 $query = $this->db->get('recovery_codes'); foreach ($query->result() as $row) { if (password_verify($input_code, $row->code_hash)) { // 驗證成功,標記恢復碼為已使用 $this->mark_code_used_by_id($row->id); return true; // 驗證成功 } } return false; // 找不到有效的恢復碼或驗證失敗 } public function mark_code_used_by_id($id) { $data = ['used' => 1]; // 標記為已使用 $this->db->where('id', $id); return $this->db->update('recovery_codes', $data); } public function generateSecureRecoveryCode($length = 16) { $characters = '23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'; // 排除易混淆字符 $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[random_int(0, $charactersLength - 1)]; } return $randomString; } } |
恢復碼實作安全性考慮
產生的恢復碼安全性已經相當高,特別是因為使用了 random_int() 和一個足夠大的字元集。
安全儲存恢復碼:如果需要在伺服器端儲存恢復碼,請確保它們是安全儲存的,最好是哈希化存儲,類似於密碼的處理方式。
安全傳輸恢復碼:如果需要將恢復碼傳送給使用者(例如透過電子郵件),請確保使用安全的通訊管道,以避免在傳輸過程中被截獲。
透過以上方法,不僅優化了產生恢復碼的函數,還進一步確保了恢復碼的安全性。
進一步安全考慮
限制嘗試次數:為了防止暴力破解,應該限制恢復碼驗證嘗試的次數。
定期更新:鼓勵使用者定期更新或在使用任何恢復碼後重新產生新的碼集,以保持帳戶安全。
透過以上方法,即可實現一個既安全又便於管理的恢復碼系統,為使用者提供了一個在丟失二步驟驗證設備時的替代登入方式。