選修單元 B EBCH3:表單、GET/POST、伺服器端有效性檢驗、曲奇、Session

本章以 HTML 表單為起點,逐步連接至 PHP 的 $_GET/$_POST 接收資料。 你將學會依情境選用 GET 與 POST、在伺服器端進行有效性檢驗,並理解曲奇(Cookie)與 Session 的用途、限制與風險。 章末提供 20 題 Coding 以鞏固整體流程。

1HTML 表單:從零建立與常用輸入元件

學習重點

  • <form> 用於收集輸入:action 指定提交目標,method 指定 GET 或 POST。
  • name 代表「提交欄位名稱」:PHP 以 $_GET["name"] 或 $_POST["name"] 取值。
  • id 主要供 CSS/JavaScript 選取、以及 <label for> 對應;不等同提交欄位。
  • 常用輸入元件:text、password、date、time(時間選擇器)、select、複選框(checkbox)、submit。

表單骨架:最少需要哪些欄位?

<form action="handle.php" method="post"> <label>姓名:<input type="text" name="studentName"></label> <button type="submit">提交</button> </form>
  • 提交目標:action 指向負責處理的 PHP 檔案。
  • 提交方法:method 決定用 $_GET 還是 $_POST 接收。
  • 欄位命名:每個需要提交的元件必須具備 name。

常用輸入元件與適用情境

元件HTML 範例常見用途注意事項
文字框 <input type="text" ...> 姓名、關鍵字、一般文字 仍需伺服器端有效性檢驗(例如長度)
密碼框 <input type="password" ...> 密碼輸入 只遮蔽顯示,不代表安全;不可把明文密碼存入曲奇
日期 <input type="date" ...> 生日、日期選擇 不同瀏覽器呈現略異;伺服器端仍要檢查格式
時間 <input type="time" ...> 預約時間、上課開始時間 伺服器端仍要檢查格式與合理範圍(例如只接受 08:00–18:00)
下拉式選單 <select name="...">...</select> 班別、選項限制輸入範圍 伺服器端應用 in_array() 驗證值是否在允許清單
複選框 <input type="checkbox" ...> 興趣多選 多值建議用 name="x[]",伺服器端以陣列接收
提交按鈕 <button type="submit">...</button> 提交表單 可配合必填提示,但不可取代伺服器端檢查

代碼小練習:表單實作(2 個任務)

每個任務均提供:提示/核對答案/顯示參考答案。請在下方以分頁切換任務,逐題完成。

任務 1:重組示範表單(填寫 action/method/class)

  • 只修改標示為 TODO 的位置:action、method、以及每個 label 的 class。
  • 完成後按「核對答案」:系統會檢查表單是否使用指定的提交位置及排版類別。

2GET 與 POST 的選用原則

重點

  • GET 把參數放在劃一資源定位(URL)的查詢字串;POST 把參數放在請求主體(request body)。
  • GET 參數通常可見,搜尋或篩選結果較容易加入書籤及分享;POST 提交內容一般不會成為網址的一部分。
  • GET 受網址長度限制;POST 通常可傳送較多數據,並較適合建立、更新或提交表單。
  • POST 本身不等於加密;敏感數據仍須透過 HTTPS 傳輸,並在伺服器端作有效性檢驗及權限檢查。

由目的決定請求方法

選擇 GET 或 POST 時,應先判斷請求的目的,而不是只看表單外觀。搜尋、排序、篩選與分頁通常只是讀取資料,而且用戶可能需要收藏或分享結果,所以 GET 較自然。登入、註冊、上載內容,以及新增、更新或刪除紀錄,通常會提交較多數據或改變伺服器狀態,因此一般採用 POST。

兩者最明顯的分別是參數位置。GET 參數成為 URL 的一部分,容易在地址列、瀏覽器歷史、伺服器日誌及分享連結中出現;POST 參數不直接顯示於 URL,但這只改變傳送位置,並沒有提供加密。敏感數據必須經 HTTPS 傳輸,伺服器亦要核對登入狀態、權限及輸入是否符合要求。

PHP 以 $_GET 或 $_POST 取得提交內容後,仍應把所有值視為外來輸入。程式應先作伺服器端有效性檢驗,再按用途處理;當內容要輸出至 HTML 時,使用適當輸出編碼,例如 htmlspecialchars(),可避免數據被瀏覽器誤解為標記。

差異、優缺點與適用情境(表格)

比較項GETPOST
參數位置URL 查詢字串(可見)request body(URL 不直接顯示)
可分享/可書籤容易(URL 包含參數)一般不便(URL 不含提交內容)
私隱與保安較弱:容易被記錄於瀏覽器歷史、伺服器日誌不直接顯示於 URL,但 POST 本身不等於加密;敏感數據仍須使用 HTTPS
數據量受 URL 長度限制一般可提交較多資料
常見用途搜尋、排序、篩選、分頁登入、註冊、提交表單、更新/刪除操作

選用原則:以問題導向作判斷

  1. 需要分享或收藏結果嗎?若需要,通常選 GET(例如搜尋結果)。
  2. 包含敏感資料嗎?若包含(例如密碼、身份數據),一般以 POST 提交,並必須配合 HTTPS;POST 本身不會加密。
  3. 資料是否會改變伺服器狀態?若會(新增/更新/刪除),一般選 POST(或其他方法)。
  4. 數據量是否較大?若較大,通常選 POST。

即時示範:GET 搜尋(顯示輸入)

Checkpoint:GET 與 POST 的選用原則(≥40 題)

本小測以四選一為主,涵蓋情境判斷、URL 可見性、長度限制與私隱風險。

3伺服器端有效性檢驗:不可只依賴客戶端

重點

  • 客戶端檢查可改善界面體驗,但可以被停用、修改或繞過;伺服器端必須重新檢查。
  • 有效性檢驗可包括:存在、數據類型、範圍、格式、長度,以及是否屬於白名單。
  • 檢查次序通常由基本條件開始,再檢查較具體規則,並只在全部通過後處理數據。
  • 有效性檢驗判斷數據是否合理;校驗則確認輸入是否與原始資料一致,兩者不可混淆。

所有外來輸入均需重新檢查

瀏覽器端的必填提示、數字輸入框及下拉式選單,能讓用戶較早發現問題,但這些限制只存在於客戶端。請求可由其他程式送出,界面設定亦可被改動,因此伺服器收到數據後必須根據同一套業務規則重新作有效性檢驗,不能因為表單看似受限制便直接信任。

檢驗規則應與欄位用途相配合。文字可檢查存在、數據類型、最短及最長長度;數值可檢查能否正確轉換及是否在合理範圍;固定選項應以白名單限制;有既定結構的內容則可檢查格式。程式應先完成基本檢查,再執行數據庫操作、計算或輸出,避免無效數據進入後續流程。

有效性檢驗只表示數據符合規則,不代表內容真實、身份已確認或操作已獲授權。校驗則著重輸入與原始資料是否一致。實際系統仍要另外處理身份核對、權限控制、輸出編碼及參數化查詢等防護。

常用函數速查:檢查甚麼、回傳甚麼、常見陷阱

函數檢查內容回傳值(重點)常見陷阱/注意簡例
isset($x) 變量是否存在且不為 null 布爾值:存在且非 null → true 只代表「存在」,不代表「不空」 if(isset($_POST['name'])) ...
empty($x) 是否為「空值」 布爾值:空 → true "0" 亦視為空;若 0 合法,應改用更精準判斷 if(empty($_POST['age'])) ...
in_array($v,$arr) 值是否在允許清單(白名單)內 布爾值:找到 → true 必要時使用第三參數 true 作嚴格比較 in_array($cls,$allow,true)
is_numeric($x) 是否可視為數值 布爾值 通過後仍建議轉型(例如 (int))再作範圍檢查 if(!is_numeric($age)) ...
is_string($x) 是否為字串(string) 布爾值 表單輸入多數是字串;更常用的是長度/格式/白名單 if(is_string($name)) ...
strlen($s) 字串長度 整數 用於最少/最多字元限制;輸入前可先 trim() if(strlen($pw)<8) ...
strpos($s,$sub) 子字串首次出現的位置 整數位置或 false 0 代表「在第一個位置找到」;判斷找不到必須用 === false if(strpos($email,'@')===false) ...

代碼小練習:以多個函數完成輸入檢查(填空題|7 個任務)

請以分頁切換任務。每個任務均提供:提示/核對答案/顯示參考答案/講解;請只修改題目標示為 TODO 的位置。

任務 1:用 isset() 檢查欄位是否存在

  • 在伺服器端先確認必須欄位(例如 user、age)是否存在於 $_POST。
  • 如缺少欄位,請把訊息加入 $errors,並輸出錯誤清單。

Checkpoint:伺服器端有效性檢驗(≥40 題)

本小測以四選一為主,包含:函數配對、常見陷阱(特別是 strpos())、以及程式片段判讀。

4曲奇(Cookie):用途、好處與壞處、適用與不適用情境

重點

  • 曲奇是由網站要求瀏覽器保存的小型數據,主要儲存在瀏覽器端,日後符合條件的請求可把它帶回伺服器。
  • 曲奇包含名稱、值及有效期等資料;路徑、網域與安全屬性會影響何時傳送及能否被腳本讀取。
  • 常見用途包括記錄語言、主題等偏好,以及保存不透明的登入狀態識別碼。
  • 曲奇容量有限,並涉及追蹤與私隱;不可把密碼、身份號碼或其他敏感數據以明文存入曲奇。

曲奇如何協助網站記住狀態

HTTP 請求本身不會自動記住上一頁發生的事情。曲奇讓瀏覽器保存少量名稱與值,並在符合網域、路徑及有效期等條件時帶回網站,因此適合保存語言、界面主題、字體大小及是否看過提示等偏好。曲奇不是大型儲存空間,內容應保持精簡。

曲奇位於瀏覽器端,用戶可以刪除,內容亦可能被改動,所以伺服器不可把曲奇值直接當作可信權限。登入系統較常把一個難以猜測的 Session identifier 放在曲奇,再由伺服器端查找實際狀態。敏感數據不可用明文保存;傳輸亦應使用 HTTPS,並按需要設定 Secure、HttpOnly 及 SameSite 等屬性。

設計曲奇時還要考慮有效期、路徑、網域、容量及私隱。有效期過長會增加資料被長期保留的風險;追蹤用途可能需要取得適當同意;刪除時若路徑或網域與建立時不一致,原有曲奇可能仍然存在。

曲奇可存甚麼?不應存甚麼?

  • 適合:偏好設定(例如字體大小、界面主題)、非敏感標記(例如「已完成新手導覽」)。
  • 不適合:明文密碼、身份證號碼等敏感資料;以及會直接影響權限的關鍵資料(除非配合簽署/伺服器端驗證)。

基本用法:設置與讀取

<?php // 設置:把鍵值寫入曲奇(實務上可加入有效期等參數) setcookie("username", "amy", time() + 86400); // 讀取:從 $_COOKIE 取得 $u = $_COOKIE["username"] ?? ""; echo "歡迎," . htmlspecialchars($u); ?>

即使資料來自曲奇,仍應視為外來輸入:必要時要做伺服器端驗證,輸出前亦要使用 htmlspecialchars()。

清除指定曲奇:把有效期設為過去

<?php // 刪除:把同名曲奇的有效期設為過去(並確保 path 與建立時一致) setcookie("username", "", time() - 3600, "/"); ?>

要成功刪除曲奇,path/domain 等參數必須與原本設置時一致;否則可能出現「看似刪除,但實際上仍存在」的情況。

互動示範:以曲奇(Cookie)記住使用者名稱(可試玩)

情境:網站希望在使用者下次回訪時顯示「歡迎回來,使用者名稱」。 請在下方模擬器輸入名稱並提交,然後再次提交或按「執行」,觀察 $_COOKIE 的變化;再按「清除曲奇」。

注意:此模擬器為教學用途,setcookie() 在同一次執行中亦會即時反映於 $_COOKIE(與真實伺服器環境「下次請求才會讀到」的行為略有差異)。

Checkpoint:曲奇(Cookie)(≥40 題)

本小測以四選一為主,涵蓋用途、好處/壞處、以及情境判斷(應否使用曲奇)。

5Session:概念、基本用法與曲奇的分別

重點

  • Session 的主要狀態保存在伺服器端;瀏覽器通常只保存並傳回一個 Session identifier。
  • 基本流程是 session_start()、讀寫 $_SESSION、檢查登入狀態,並在登出時清除資料並使識別碼失效。
  • Session 有生命週期;應設定合理逾時,並在敏感操作後更新識別碼及重新核對權限。
  • Session 並非天生安全:識別碼若被竊取、固定或錯誤共用,仍可能導致冒用,因此需要 HTTPS 及其他防護。

以識別碼連接瀏覽器與伺服器狀態

Session 用來解決 HTTP 請求之間缺乏持續狀態的問題。伺服器為工作階段建立一筆狀態資料,瀏覽器則通常以曲奇保存 Session identifier。之後每次請求帶回識別碼,伺服器便可找回登入用戶、購物車或流程進度等資料,而毋須把完整狀態交給瀏覽器保存。

Session 的使用並不止於呼叫 session_start()。登入成功後應只保存必要資料,受保護頁面必須在每次請求重新檢查身份與權限;登出時要清除伺服器端狀態並處理識別曲奇。系統亦應設定合理的閒置逾時與最長生命週期,避免長期未使用的工作階段繼續有效。

伺服器端保存狀態不代表 Session 自動安全。若識別碼經不安全連線洩露、被固定、被複製或未被妥善設為失效,其他人仍可能冒用工作階段。因此登入流程應使用 HTTPS,設定合適的曲奇屬性,在登入或權限改變後更新識別碼,並把真正權限判斷保留在伺服器端。

甚麼是 Session?為何常用於登入?

HTTP 請求本質上無狀態;若要「記住」某位用戶已通過身份核對,便需要保存狀態。 Session 把狀態資料放在伺服器端,通常只把一個識別碼交由瀏覽器保存並帶回,因此比把完整狀態直接放在曲奇更合適用於登入。

基本流程(示意)

  1. 在需要使用 Session 的頁面最上方呼叫 session_start()。
  2. 登入成功後:設定 $_SESSION["logged_in"] = true,並保存必要的用戶識別資料(例如 user_id)。
  3. 受保護頁面:先檢查 $_SESSION 是否存在登入標記;不符合則導向登入頁。
  4. 登出:清除 Session(例如 session_unset() 或 session_destroy())。

代碼示範:登入、受保護頁面與登出

以下以三個檔案示範最基本流程:登入成功後建立 Session;受保護頁面先檢查 Session;登出時清除 Session。實務上仍需配合 HTTPS、伺服器端有效性檢驗及更嚴謹的權限控制。

login.php(建立 Session)

<?php session_start(); if (!empty($_POST)) { $user = trim($_POST['user'] ?? ''); $pw = trim($_POST['pw'] ?? ''); // 示範:以固定帳號/密碼判斷(實務應使用數據庫與雜湊) if ($user === 'student' && $pw === 'abc') { $_SESSION['logged_in'] = true; $_SESSION['username'] = $user; header('Location: welcome.php'); exit; } else { $error = '帳號或密碼錯誤。'; } } ?> <form method="post"> <label class="inline-input">帳號:<input name="user"></label> <label class="inline-input">密碼:<input type="password" name="pw"></label> <button class="btn" type="submit">登入</button> </form> <?php if (!empty($error)) echo '<p>' . htmlspecialchars($error) . '</p>'; ?>

welcome.php(檢查 Session)

<?php session_start(); if (empty($_SESSION['logged_in'])) { header('Location: login.php'); exit; } $u = $_SESSION['username'] ?? ''; ?> <p>歡迎,<strong><?php echo htmlspecialchars($u); ?></strong>!</p> <p><a href="logout.php">登出</a></p>

logout.php(清除 Session)

<?php session_start(); session_unset(); session_destroy(); header('Location: login.php'); exit; ?>

互動示範:以 Session 保存登入狀態(可試玩)

情境:網站需要在多次 HTTP 請求之間保存「已登入」狀態。 請在下方模擬器使用測試帳號登入,觀察登入後內容如何由 $_SESSION 控制;再按「登出」清除 Session。

此示範把「登入頁面」與「會員區」寫在同一檔案內,以便在模擬器中試玩;在真實網站中可拆成多頁並配合導向(redirect)流程。

Session 與曲奇:如何比較?

項目曲奇(Cookie)Session
主要儲存位置客戶端(瀏覽器)伺服器端
資料可否被用戶改動較容易(可被修改)較難(用戶通常只持有 Session ID)
常見用途偏好、非敏感狀態登入狀態、權限控制
保安重點不應存敏感資料;內容需驗證保護 Session ID、避免被盜用;伺服器端設計需嚴謹

Checkpoint:Session 概念與流程(≥40 題)

本小測以四選一為主,涵蓋 Session 基本概念、session_start()、$_SESSION 用法,以及 Session vs 曲奇分辨。

6防禦性安全:輸入、輸出、數據庫與錯誤訊息

重點

  • 所有外來輸入都要在伺服器端作有效性檢驗,不能只信任表單界面或客戶端腳本。
  • 把數據輸出至 HTML 前應作情境相符的輸出編碼,例如使用 htmlspecialchars() 降低 XSS 風險。
  • 存取數據庫時應使用參數化查詢,把 SQL 指令與外來數值分開處理。
  • 只收集、保存及顯示完成任務所需的最少數據,並避免向用戶顯示詳細系統錯誤。

安全控制必須配合數據所處位置

防禦性安全不是在表單加上一項檢查便完成,而是由接收輸入開始,一直延伸至權限判斷、數據庫存取、畫面輸出及錯誤處理。客戶端檢查可改善界面,但伺服器端仍要按存在、類型、範圍、格式、長度及白名單等規則作有效性檢驗,並確認目前用戶有權執行操作。

不同防護處理不同問題。參數化查詢把 SQL 結構與外來數值分開,適用於數據庫操作;輸出編碼則在資料進入 HTML、屬性或其他輸出情境前轉換特殊字符,以降低 XSS 風險。兩者不能互相取代,也不應只依賴一個通用「清理」函數。

系統應遵守最少數據原則,只收集、保存及顯示完成任務所必需的內容。錯誤發生時,對用戶提供可採取行動但不洩露內部結構的訊息;詳細 SQL、檔案路徑及堆疊追蹤應記錄於受保護的伺服器日誌,而不是直接顯示在網頁。

表單安全:以白名單有效性檢驗為核心

前端可提供下拉式選單限制輸入,但用戶仍可自行構造請求提交未列於選單的值。 因此伺服器端應把可接受的值列入「允許清單」,並以 in_array() 等方法檢查;不符合即拒絕處理。

<?php $allow = ["1A","1B","1C"]; $cls = $_POST["class"] ?? ""; if (!in_array($cls, $allow, true)) { die("班別不在允許清單內。"); } ?>

XSS 基本概念:為何要做輸出前處理?

若把用戶輸入直接輸出到網頁,輸入內容可能被瀏覽器當作 HTML/JavaScript 解讀,造成跨網站腳本攻擊(XSS)。 基本防線是:在輸出前使用 htmlspecialchars() 把特殊字元轉換為安全形式。

<?php $msg = $_GET["msg"] ?? ""; echo "<p>留言:" . htmlspecialchars($msg) . "</p>"; ?>

錯誤訊息設計:既要清楚,又要避免洩露細節

  • 給用戶看的訊息:指出哪個欄位不符合要求,以及如何修正(例如「密碼最少 8 字元」)。
  • 避免洩露:不要直接輸出伺服器路徑、數據庫錯誤全文、或系統堆疊追蹤。
  • 給開發者看的細節:可記錄到伺服器日誌(本課程先理解概念即可)。

Checkpoint:延伸課題(≥40 題)

本小測以四選一為主,涵蓋:白名單驗證、輸出前處理(XSS 概念)、以及錯誤訊息的設計原則。

✍️頁末 20 題 Coding(一行一題)

每題均提供:提示/核對答案/顯示參考答案/講解。請在模擬器內完成代碼,然後按「核對答案」。

代碼題 1:表單(text):問候語
代碼題 2:表單(password):只顯示密碼長度
代碼題 3:表單(date):輸出生日
代碼題 4:下拉式選單:顯示班別
代碼題 5:複選框:列出興趣(foreach)
代碼題 6:GET 搜尋:從 URL 取得 q
代碼題 7:POST 登入:必填檢查
代碼題 8:isset():欄位是否存在
代碼題 9:empty():必填欄位
代碼題 10:is_numeric():年齡必須為數字
代碼題 11:strlen():密碼最少 8 字元
代碼題 12:in_array():白名單驗證下拉值
代碼題 13:strpos():電郵必須包含 @
代碼題 14:曲奇:記住使用者名稱 1 天
代碼題 15:曲奇:清除指定 cookie
代碼題 16:Session:登入後建立已登入狀態
代碼題 17:Session:未登入則導回登入頁
代碼題 18:登出:清除 Session
代碼題 19:同一處理邏輯支援 GET/POST
代碼題 20:綜合:提交→有效性檢驗→成功才設定 Session

總結 課文腦圖總結

重點:

  • 表單只會提交具有 name 的控制項;id、class 與 label for 有不同用途。
  • GET 適合可分享的讀取與搜尋;POST 適合登入及會改變狀態的操作,但 POST 本身並不等於加密。
  • 伺服器端有效性檢驗應按「存在 → 結構 → 必填 → 類型/格式 → 長度/範圍 → 允許清單」逐層進行。
  • 曲奇適合少量非敏感偏好;登入及權限狀態應由伺服器端 Session 保存,輸出任何外來值前仍要安全轉義。

本腦圖把全課重新分類為「數據生命週期、表單控制項、GET/POST、有效性檢驗、函數陷阱、安全輸出、曲奇、Session 及整合應用」,並非照搬原網頁章節次序。

閱讀時可先從根節點展開完整流程,再利用「樹狀圖導覽」直接跳到 strpos() 的 0/false 陷阱、曲奇屬性、Session 登入流程或任何代碼題。

聚焦模式會以動畫移到所選節點並調整縮放;所有學生修改只暫存在瀏覽器本機,亦可匯出為 .ictmap。