三大超全域變數對比
| 特性 | $_GET | $_POST | $_REQUEST |
| 資料來源 | URL 查詢字串 | HTTP Request Body | GET + POST + COOKIE (順序由 variables_order 決定) |
| 可見度 | URL 明文顯示 | Body 中,不顯示於 URL | 混合來源 |
| 長度限制 | ~2000 字元 (瀏覽器/伺服器限制) | 受 post_max_size (預設 8M) | 取決於實際來源 |
| 編碼方式 | application/x-www-form-urlencoded | application/x-www-form-urlencoded / multipart/form-data | 同上 |
| 適用場景 | 搜尋、分頁、篩選、書籤分享、非敏感資料 | 登入、註冊、上傳、修改/刪除、敏感資料 | 不建議直接使用 (來源模糊) |
| 安全性 | 低 (歷史紀錄、Referer、日誌洩露) | 較高 (Body 不易外洩) | 最低 (Cookie 污染風險) |
| 快取/書籤 | 支援 | 不支援 | 視來源而定 |
| 瀏覽器後退 | 正常 | 提示重新提交 | 視來源而定 |
實戰範例
GET 表單 (搜尋頁)
<form action="search.php" method="get">
關鍵字: <input type="search" name="q" value="= htmlspecialchars($_GET['q'] ?? '') ?>">
分類: <select name="cat"><option value="">全部</option>...</select>
<button type="submit">搜尋</button>
</form>
<?php // search.php
$q = $_GET['q'] ?? '';
$cat = $_GET['cat'] ?? '';
// 建構 SQL WHERE 子句...
?>
POST 表單 (會員登入)
<form action="login.php" method="post">
<input type="hidden" name="csrf_token" value="= $_SESSION['csrf_token'] ?>">
帳號: <input name="username" autocomplete="username" required>
密碼: <input type="password" name="password" autocomplete="current-password" required>
<button type="submit" name="action" value="login">登入</button>
</form>
<?php // login.php
if (($_POST['action'] ?? '') === 'login') {
// 驗證 CSRF
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')) {
exit('非法請求');
}
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
// 驗證邏輯...
}
?>
$_REQUEST 的風險與棄用建議
<?php
// 危險範例:Cookie 覆蓋 POST 參數
// variables_order = "GPC" (預設)
// 攻擊者設定 Cookie: admin=1
// 正常用戶 POST: action=delete&id=5
// $_REQUEST['admin'] === '1' (來自 Cookie) -> 權限繞過風險
// 安全做法:明確指定來源
$action = $_POST['action'] ?? $_GET['action'] ?? null; // 明確優先順序
$admin = $_SESSION['is_admin'] ?? false; // 權限只信 Session
?>
進階:檔案上傳 ($_FILES)
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="avatar" accept="image/*">
<button>上傳</button>
</form>
<?php
$file = $_FILES['avatar'] ?? null;
if ($file && $file['error'] === UPLOAD_ERR_OK) {
// 驗證 MIME (finfo)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($mime, $allowed)) exit('檔案類型不允許');
// 隨機檔名 + 移動
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
$dest = __DIR__ . '/uploads/' . $newName;
move_uploaded_file($file['tmp_name'], $dest);
// 記錄資料庫...
}
?>
配置參數調優
; php.ini
post_max_size = 20M ; POST 總體大小上限
upload_max_filesize = 10M ; 單檔上限
max_file_uploads = 20 ; 同時上傳檔案數
max_input_vars = 3000 ; 最大輸入變數數量 (防 HashDoS)
max_input_time = 60 ; 解析輸入資料最長秒數