PHP 表單協定與防禦邊界

摒棄「安不安全」的模糊說法,精準定位 HTTP 封包物理位置與記憶體斷言機制。

一、 HTTP 傳輸機制:GET 與 POST 的物理對抗

GET 方法:網址列暴露

Query String

狀態語意:冪等讀取。 向伺服器索取資料,不改變伺服器狀態。

  • 物理位置:資料直接附加在 URL 網址列後方 (例如 ?keyword=apple&page=2)。
  • 使用時機:搜尋、篩選、分頁。因為它可被瀏覽器加入書籤、分享連結,且可被快取 (Cache)。
  • 注意事項:受限於瀏覽器 URL 長度 (約 2KB)。絕對不可傳輸密碼,因為資料會明文留在瀏覽器歷史紀錄與伺服器 Log 中。
<!-- HTML 寫法 -->
<form method="GET" action="search.php">
    <input type="text" name="keyword">
</form>

// PHP 接收法
$keyword = $_GET['keyword'];

POST 方法:本體封裝

Request Body

狀態語意:狀態突變 (Mutation)。 執行會改變伺服器狀態的動作。

  • 物理位置:資料封裝在 HTTP 請求的本體 (Body) 中,URL 完全看不見。
  • 使用時機:登入、註冊、修改資料庫、上傳檔案。沒有嚴格的大小限制。
  • 注意事項:無法加入書籤、不可被快取。若使用者按下「重新整理」,瀏覽器會彈出「確認重新提交表單」的警告,以防重複扣款或重複寫入。
<!-- HTML 寫法 -->
<form method="POST" action="login.php">
    <input type="password" name="pwd">
</form>

// PHP 接收法
$password = $_POST['pwd'];

二、 防禦邊界對抗:Client vs Server

客戶端驗證 (Client-side / JS & HTML5):
使用 HTML 的 requiredminlength 或是 JavaScript 的 onsubmit 檢查。這只是為了 UX (使用者體驗),減少伺服器不必要的運算。但它是「可被繞過的假性防禦」。駭客可以輕易禁用瀏覽器 JS,或使用 cURL / Postman 直接構造 HTTP 封包攻擊伺服器。

伺服器端驗證 (Server-side / PHP):
系統的 「絕對物理防禦邊界」。無論前端怎麼檢查,後端必須假設「所有接收到的數據都是惡意的」,重新進行變量斷言。

後端防禦武器庫 (PHP Validation Filters)

1. 虛無判定

empty()

防禦「必填欄位缺失」。檢查變數是否不存在、為空字串 ""0null

if (empty($_POST['username'])) {
    die("錯誤:使用者名稱不能為空");
}

2. 長度坍縮預防

strlen()

防禦「緩衝區溢位」或「資料庫截斷」。確保字串長度在合理範圍內。

$pwd = $_POST['password'];
if (strlen($pwd) < 8) {
    die("錯誤:密碼長度過短");
}

3. 型別斷言

is_numeric / is_string

防禦「SQL 注入」的基礎。確保應該是數字的變數 (如 ID、年齡) 絕對不包含惡意字元。

if (!is_numeric($_POST['age'])) {
    die("錯誤:年齡必須是純數字");
}

4. 白名單機制

in_array()

最高級別的權限防禦。只允許用戶輸入你預先定義好的安全值,捨棄未知的惡意輸入。

$allowed = ['user', 'admin'];
if (!in_array($_POST['role'], $allowed)) {
    die("錯誤:非法的權限角色");
}

5. 特徵掃描陷阱 (弱型別漏洞)

strpos() === false

strpos() 用於尋找子字串的索引位置。致命漏洞:如果目標字元在字串的第一個位置 (Index 0),PHP 會回傳 0。但在鬆散比對中,0 == false 成立,會導致邏輯誤判。因此必須使用嚴格型別比對 ===!==

$email = "@attack.com"; // @ 在 index 0

// 錯誤寫法 (會誤以為找不到 @):
if (strpos($email, '@') == false) { die("無效信箱"); } 

// 正確寫法 (嚴格比對型別與值):
if (strpos($email, '@') === false) { die("無效信箱"); }

三、 狀態持久化:Cookie 的機制與代價

物理機制: HTTP 協定天生「失憶 (無狀態)」,伺服器無法分辨連續兩次請求是否來自同一人。setcookie() 是一道透過 HTTP Response Header 發送的指令,強迫客戶端瀏覽器在本地硬碟存下一段「鍵值對 (Key-Value)」。未來瀏覽器對該網域發送的每一個請求,都會自動挾帶此 Cookie。


優點 (好處)

  • 突破無狀態限制:實現購物車、維持登入狀態、記住使用者偏好 (如語系、深色模式)。
  • 降低伺服器記憶體負擔:將狀態資料分散儲存在用戶端的硬碟中,而非佔用伺服器 (Server Session) 的 RAM。

缺點 (壞處/漏洞)

  • 信任危機與篡改:存於用戶端即為明文,用戶可輕易透過 DevTools 修改數值 (例如把 role=user 改為 role=admin)。機密資料絕對不可放入。
  • XSS 竊取風險:若未設定 HttpOnly 標籤,惡意 JavaScript 可以輕易讀取並盜走用戶的登入 Cookie。
  • 容量限制:每個 Domain 最多只能存儲約 4KB 的資料。

四、 HTTP 與伺服器對抗實驗室

嘗試在左側表單輸入不同數據,選擇傳輸協定 (GET/POST) 並點擊「發送」。觀察中間的「HTTP 網路封包」差異,並點擊右側「Step」觀測伺服器如何進行斷言攔截。

客戶端 (Browser Form)
網路傳輸層 (HTTP Packet)
等待表單發送...
PHP 伺服器防禦引擎
// 程式碼將依據 GET/POST 動態生成
引擎執行日誌 (Engine Log)