myAdBanner

2026年5月12日 星期二

php remember me 正規寫法

 

在 PHP 中實現「記住我」(Remember Me)功能,絕對不能直接將帳號或密碼存入 Cookie。目前業界公認最安全的「正規寫法」是採用雙重權限驗證(Selector & Validator)機制。
這種方法將原本的一個憑證拆成兩部分,即便資料庫外洩,攻擊者也無法輕易仿造 Cookie;即便 Cookie 被竊取,也能透過「一次性驗證碼」機制偵測到異常。
1. 資料表準備
你需要在資料庫中準備一張專用的 user_tokens 表,用來儲存持久化的登入狀態。
  • id: 流水號。
  • selector: 長度約 12 字元的隨機字串(作為資料庫查詢索引,唯一值)。
  • validator_hash: 驗證碼的雜湊值(類似密碼雜湊)。
  • user_id: 對應的使用者 ID。
  • expires_at: 到期時間。
2. 登入成功時:產生 Cookie
當使用者勾選「記住我」時,執行以下流程:
  1. 產生隨機數:產生兩個強隨機字串(建議使用 random_bytes())。
    • $selector: 查詢標籤(例如 12 位元)。
    • $validator: 實際驗證金鑰(例如 32 位元)。
  2. 存入資料庫:將 $selector 與 雜湊後 的 $validator_hash(使用 password_hash())存入表中。
  3. 發送 Cookie:將 selector:validator 組合成字串存入使用者瀏覽器。
    • 安全性設定:必須開啟 HttpOnly(防 XSS)、Secure(限 HTTPS)與 SameSite=Lax。
3. 自動登入時:驗證流程
當使用者帶著 Cookie 重新訪問網站,且 Session 已過期時:
  1. 解析 Cookie:將內容拆回 $selector 與 $validator。
  2. 查詢資料庫:根據 $selector 找出一筆未過期的紀錄。
  3. 比對驗證碼:使用 password_verify($validator, $row['validator_hash']) 檢查是否匹配。
  4. 重新核發(推薦):為了防範 Cookie 側錄(Session Hijacking),驗證成功後應刪除舊紀錄並核發一組全新的 Selector/Validator 給使用者。
為什麼這比存 ID/密碼安全?
  • 資料庫外洩防護:資料庫只存 Validator 的雜湊值。駭客即便拿到資料表,也無法反推回原始 Cookie 內容。
  • 防止計時攻擊:透過 Selector 快速定位,再使用時序安全的函數比對 Validator。
  • 降低風險範圍:這個 Token 應僅限於「自動登入」。當使用者要執行修改密碼、結帳等敏感操作時,應強制要求重新輸入完整密碼。
對於更大型的系統,開發者通常會選用如 Laravel Auth 或 Symfony Remember Me 等成熟框架,它們內部已內建上述的安全實作。
這是一個基於 Selector & Validator 機制的實作範例。這個寫法能確保即便資料庫被脫庫(Dump),攻擊者也無法偽造 Cookie。
1. 資料庫結構 (MySQL)
sql
CREATE TABLE user_tokens (
    id INT AUTO_INCREMENT PRIMARY KEY,
    selector CHAR(12) NOT NULL,
    validator_hash CHAR(64) NOT NULL,
    user_id INT NOT NULL,
    expires_at DATETIME NOT NULL,
    UNIQUE(selector)
);

2. 登入成功時:產生並存儲 Token
當用戶勾選「記住我」並登入成功後,執行此段程式碼:
php
// 1. 產生隨機 Token
$selector = bin2hex(random_bytes(6)); // 12 字元
$validator = bin2hex(random_bytes(32)); // 64 字元
$token = $selector . ':' . $validator;

// 2. 設定過期時間 (例如 30 天)
$days = 30;
$expiresAt = date('Y-m-d H:i:s', time() + ($days * 86400));

// 3. 存入資料庫 (只存雜湊後的 Validator)
$validatorHash = password_hash($validator, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO user_tokens (selector, validator_hash, user_id, expires_at) VALUES (?, ?, ?, ?)");
$stmt->execute([$selector, $validatorHash, $userId, $expiresAt]);

// 4. 發送 Cookie (安全性設定至關重要)
setcookie('remember_me', $token, [
    'expires' => time() + ($days * 86400),
    'path' => '/',
    'domain' => '', // 填寫你的域名
    'secure' => true,     // 僅限 HTTPS
    'httponly' => true,   // 防止 JS 讀取 (防 XSS)
    'samesite' => 'Lax'   // 防止 CSRF
]);
3. 使用者回訪時:驗證 Token
當 $_SESSION['user_id'] 不存在,但有 $_COOKIE['remember_me'] 時觸發:

if (empty($_SESSION['user_id']) && !empty($_COOKIE['remember_me'])) {
    $parts = explode(':', $_COOKIE['remember_me']);
    
    if (count($parts) === 2) {
        [$selector, $validator] = $parts;

        // 1. 根據 Selector 查找
        $stmt = $pdo->prepare("SELECT * FROM user_tokens WHERE selector = ? AND expires_at > NOW()");
        $stmt->execute([$selector]);
        $tokenRecord = $stmt->fetch();

        // 2. 比對 Validator 雜湊值
        if ($tokenRecord && password_verify($validator, $tokenRecord['validator_hash'])) {
            
            // 驗證成功!登入使用者
            $_SESSION['user_id'] = $tokenRecord['user_id'];

            // 3. 重要:輪替 Token (防止側錄攻擊)
            // 刪除舊的,並重新執行上面的「產生並存儲 Token」步驟發送新 Cookie
            $stmt = $pdo->prepare("DELETE FROM user_tokens WHERE id = ?");
            $stmt->execute([$tokenRecord['id']]);
            
            // (此處呼叫步驟 2 的邏輯產生新 Token...)
        }
    }
}
 

4. 登出時:清除 Token
登出不只是刪除 Session,也要記得清掉資料庫的紀錄。
// 刪除資料庫紀錄
if (!empty($_COOKIE['remember_me'])) {
    $parts = explode(':', $_COOKIE['remember_me']);
    $selector = $parts[0];
    $stmt = $pdo->prepare("DELETE FROM user_tokens WHERE selector = ?");
    $stmt->execute([$selector]);
}

// 刪除 Cookie
setcookie('remember_me', '', time() - 3600, '/');
 

2026年5月7日 星期四

xampp 設定 Apache VirtualHost(虛擬主機)

 

Apache VirtualHost(虛擬主機)

設定完之後,你會從:

http://localhost/project1

變成:

http://project1.local

這對 MVC 專案非常重要。

因為:

  • 路徑比較正常
  • Router 比較不會壞
  • AJAX 好處理
  • Cookie/session 好管理
  • 未來 API 比較穩

一、先確認你的環境

你目前大概是:

  • XAMPP
  • AppServ
  • WAMP
  • 自架 Apache

其中之一。


二、VirtualHost 的原理

Apache 會:

project1.local
↓
對應到
↓
D:\web\project1\

三、設定步驟(Windows)

我用最通用方式教你。


Step 1:建立專案目錄

例如:

D:\web\

裡面:

D:\web\project1
D:\web\opencart
D:\web\ci3

Step 2:修改 hosts 檔

Windows:

C:\Windows\System32\drivers\etc\hosts

用:

系統管理員身份

開啟記事本。


加入:

127.0.0.1 project1.local
127.0.0.1 opencart.local
127.0.0.1 ci3.local

Step 3:開 Apache VirtualHost

找到:

httpd-vhosts.conf

通常在:


XAMPP

C:\xampp\apache\conf\extra\httpd-vhosts.conf

AppServ

C:\AppServ\Apache24\conf\extra\httpd-vhosts.conf

Step 4:加入 VirtualHost

<VirtualHost *:80>
ServerName project1.local
DocumentRoot "D:/web/project1"

<Directory "D:/web/project1">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerName opencart.local
DocumentRoot "D:/web/opencart"

<Directory "D:/web/opencart">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerName ci3.local
DocumentRoot "D:/web/ci3"

<Directory "D:/web/ci3">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

Step 5:確認 httpd.conf 有啟用 vhosts

找到:

#Include conf/extra/httpd-vhosts.conf

把:

#

拿掉:

Include conf/extra/httpd-vhosts.conf

Step 6:重啟 Apache

非常重要。


Step 7:測試

瀏覽器輸入:

http://project1.local

成功。


四、MVC 專案非常需要 .htaccess

尤其:

  • CodeIgniter
  • Laravel
  • OpenCart SEO URL

Apache 要啟用 rewrite_module

在:

httpd.conf

找到:

#LoadModule rewrite_module modules/mod_rewrite.so

拿掉:

#

變:

LoadModule rewrite_module modules/mod_rewrite.so

五、CodeIgniter 會用到 Rewrite

例如:

以前:

index.php/news/edit/3

之後:

/news/edit/3

六、VS Code 怎麼配合

之後:

File → Open Folder

開:

D:\web\project1

即可。



2024年10月22日 星期二

jquery 外掛 顯示隱藏的密碼欄位

 <script type="text/javascript" src="view/javascript/jquery/show-hide-password.min.js"></script>

 

2024年5月12日 星期日

刪除 Thumbs.db 方法

 @echo off
attrib -s -h -r Thumbs.db /s /d >nul
del Thumbs.db /s