目錄
- 一、表單的創建與提交方式
- 1.1 HTML 表單的基本結構
- 1.2 GET 和 POST 提交方式的區別及適用場景
- 二、表單數據的接收與處理
- 2.1 使用\$_GET、\$_POST 超全局變量獲取表單數據
- 2.2 對接收的數據進行驗證
- 三、表單安全處理
- 3.1 防止 XSS 攻擊的方法
- 3.2 防止 CSRF 攻擊的措施
一、表單的創建與提交方式
1.1 HTML 表單的基本結構
在 HTML 中,<form>標簽用于定義表單,它是一個容器,包含了各種表單元素,用于收集用戶輸入的數據。<form>標簽有一些重要的屬性,其中action屬性指定了表單數據提交的目標 URL 地址,method屬性則指定了提交表單數據時使用的 HTTP 方法,常見的有GET和POST。
例如:
<form action="submit.php" method="post"><!-- 表單元素將在這里添加 -->
</form>
在上述代碼中,表單數據將被提交到submit.php頁面,并且使用POST方法進行提交。
<input>元素是表單中最常用的元素之一,它可以根據type屬性的不同,呈現出多種不同的輸入類型,以滿足各種數據收集需求。
- 當type="text"時,它是一個單行文本輸入框,用于收集用戶的文本輸入,比如用戶名、昵稱等。例如:<input type=“text” name=“username” placeholder=“請輸入用戶名”>,其中name屬性用于標識這個輸入框,在后端接收數據時會用到,placeholder屬性則提供了一個提示信息,告訴用戶應該輸入什么內容。
- type="password"用于創建密碼輸入框,用戶輸入的內容會被掩碼顯示,以保護密碼安全。如:<input type=“password” name=“password” placeholder=“請輸入密碼”> 。
- type="radio"表示單選按鈕,用于讓用戶從多個選項中選擇一個。同一組單選按鈕需要有相同的name屬性值,通過value屬性來區分不同的選項。例如:
<input type="radio" name="gender" value="male" id="male">
<label for="male">男</label>
<input type="radio" name="gender" value="female" id="female">
<label for="female">女</label>
這里通過<label>標簽與<input>元素關聯,當用戶點擊<label>標簽時,對應的單選按鈕也會被選中,增強了用戶交互體驗。for屬性的值與<input>元素的id屬性值相同,用于建立這種關聯。
- type="checkbox"創建復選框,允許用戶選擇多個選項。 例如:
<input type="checkbox" name="hobby" value="reading" id="reading">
<label for="reading">閱讀</label>
<input type="checkbox" name="hobby" value="sports" id="sports">
<label for="sports">運動</label>
同樣,同一組復選框具有相同的name屬性,每個復選框通過不同的value值來標識自己,<label>標簽用于關聯和增強交互。
除了<input>元素,表單中還有其他一些常用的標簽。<textarea>標簽用于創建多行文本輸入區域,適合收集用戶較長的文本內容,如留言、評論等。通過rows和cols屬性可以設置文本區域的行數和列數,也可以通過 CSS 的width和height屬性來控制其大小。例如:
<textarea name="message" rows="4" cols="50" placeholder="請輸入您的留言"></textarea>
<select>標簽用于創建下拉選擇框,<option>標簽則定義了下拉框中的選項。可以通過selected屬性設置某個選項默認被選中。例如:
<select name="city"><option value="beijing">北京</option><option value="shanghai">上海</option><option value="guangzhou" selected>廣州</option>
</select>
<button>標簽用于創建按鈕,當type="submit"時,它和<input type=“submit”>一樣,用于提交表單數據;當type="button"時,它是一個普通按鈕,通常需要配合 JavaScript 來實現特定的功能 ,比如點擊彈出提示框等。例如:
<button type="submit">提交</button>
<button type="button" onclick="alert('這是一個普通按鈕')">點擊我</button>
1.2 GET 和 POST 提交方式的區別及適用場景
GET 和 POST 是 HTTP 協議中兩種常用的提交表單數據的方式,它們在多個方面存在區別,了解這些區別有助于我們在實際開發中根據不同的業務需求選擇合適的提交方式。
從數據傳輸位置來看,GET 方式會將表單數據附加在 URL 的查詢字符串中,以?分隔 URL 和參數,參數之間用&連接。例如,當我們在瀏覽器地址欄中輸入https://example.com/search?q=php,這里的q=php就是 GET 方式傳遞的參數。而 POST 方式則將數據放在 HTTP 請求體中進行傳輸,不會顯示在 URL 中。
數據量限制方面,GET 方式由于受 URL 長度限制(不同瀏覽器對 URL 長度限制不同,一般在 2048 個字符左右),所以能傳輸的數據量較小,一般不適合傳輸大量數據。POST 方式理論上對數據量沒有限制(實際上服務器可能會有一些配置限制,但通常比 GET 方式能傳輸的數據量大得多),適合傳輸大量的數據,比如文件上傳、長文本內容提交等場景。
安全性上,GET 方式因為數據暴露在 URL 中,容易被緩存、記錄在瀏覽器歷史記錄和服務器日志中,所以安全性較低,不適合傳輸敏感信息,如用戶密碼、銀行卡號等。POST 方式數據在請求體中,相對不那么容易被獲取到,安全性較高,但如果使用 HTTP 協議傳輸,數據仍然是明文傳輸的,在傳輸敏感信息時,建議結合 HTTPS 協議來提高安全性。
從冪等性角度來說,GET 請求是冪等的,多次發送相同的 GET 請求,對服務器資源的影響應該是相同的,即不會產生額外的副作用,只是獲取相同的資源。例如多次請求https://example.com/articles/1獲取文章內容,每次得到的結果應該是一樣的。而 POST 請求通常不是冪等的,多次提交相同的 POST 請求可能會導致不同的結果,比如多次提交訂單創建請求,可能會創建多個訂單。
在適用場景上,GET 方式常用于數據查詢操作,比如搜索功能、獲取文章列表等。因為它將參數顯示在 URL 中,方便用戶分享鏈接,并且可以被搜索引擎爬蟲抓取到。例如在電商網站中,用戶搜索商品時,使用 GET 方式提交搜索關鍵詞,方便用戶將搜索結果分享給他人,且搜索引擎也能更好地索引這些內容。POST 方式則常用于數據的創建、修改和刪除操作,以及提交敏感信息,如用戶注冊、登錄時提交用戶名和密碼,向數據庫中插入新的記錄等。比如在用戶注冊頁面,使用 POST 方式提交用戶輸入的注冊信息,包括用戶名、密碼、郵箱等敏感信息,避免這些信息暴露在 URL 中。
二、表單數據的接收與處理
2.1 使用$_GET、$_POST 超全局變量獲取表單數據
在 PHP 中,$_GET和$_POST是兩個非常重要的超全局變量,用于獲取 HTML 表單提交的數據。它們的使用方式相對簡單直觀,但在實際應用中,需要根據表單的提交方式來正確選擇使用哪個變量。
當表單的method屬性設置為GET時,我們使用$_GET變量來獲取表單數據。例如,有如下一個簡單的 HTML 表單:
<!DOCTYPE html>
<html><body><form action="process.php" method="get">用戶名:<input type="text" name="username"><input type="submit" value="提交"></form></body></html>
在process.php文件中,可以使用以下 PHP 代碼來獲取提交的用戶名:
<?php
if (isset($_GET['username'])) {$username = $_GET['username'];echo "歡迎你,$username";
}
?>
在上述代碼中,首先通過isset($_GET[‘username’])來檢查$_GET數組中是否存在名為username的鍵,這一步很重要,它可以避免在鍵不存在時產生未定義索引的錯誤。如果存在,就將其值賦給$username變量,然后進行后續的處理,這里只是簡單地輸出歡迎信息。
當表單的method屬性設置為POST時,使用$_POST變量獲取數據。比如下面這個表單用于收集用戶的登錄信息:
<!DOCTYPE html>
<html><body><form action="login.php" method="post">用戶名:<input type="text" name="username">密碼:<input type="password" name="password"><input type="submit" value="登錄"></form></body></html>
在login.php文件中,獲取表單數據的 PHP 代碼如下:
<?php
if (isset($_POST['username']) && isset($_POST['password'])) {$username = $_POST['username'];$password = $_POST['password'];// 這里可以添加驗證用戶名和密碼的邏輯,比如查詢數據庫等if ($username === 'admin' && $password === '123456') {echo "登錄成功,歡迎 $username";} else {echo "用戶名或密碼錯誤";}
}
?>
同樣,先檢查$_POST數組中username和password鍵是否存在,存在則獲取對應的值,然后進行登錄驗證邏輯。
對于一些復雜的數據,比如復選框、下拉框等,獲取方式也類似。以復選框為例,假設有一個表單用于收集用戶的興趣愛好:
<!DOCTYPE html>
<html><body><form action="hobbies.php" method="post"><input type="checkbox" name="hobbies[]" value="reading">閱讀<input type="checkbox" name="hobbies[]" value="sports">運動<input type="checkbox" name="hobbies[]" value="music">音樂<input type="submit" value="提交"></form></body></html>
在hobbies.php中獲取數據:
<?php
if (isset($_POST['hobbies'])) {$hobbies = $_POST['hobbies'];echo "你的興趣愛好有:";foreach ($hobbies as $hobby) {echo $hobby . " ";}
}
?>
由于復選框可以選擇多個值,所以name屬性設置為hobbies[],這樣在 PHP 中通過$_POST[‘hobbies’]獲取到的是一個數組,包含了用戶選擇的所有興趣愛好值,通過遍歷數組可以將其一一輸出展示。
2.2 對接收的數據進行驗證
對表單接收的數據進行驗證是確保數據質量和安全性的重要步驟。下面介紹幾種常見的驗證方式及其在 PHP 中的實現。
非空驗證是最基本的驗證方式之一,用于確保用戶沒有留空必填字段。在 PHP 中,可以使用empty()函數來進行非空驗證。例如,對于上面登錄表單中的用戶名和密碼字段:
<?php
if (empty($_POST['username'])) {echo "用戶名不能為空";exit;
}
if (empty($_POST['password'])) {echo "密碼不能為空";exit;
}
// 后續處理代碼
?>
這里通過empty($_POST[‘username’])檢查username字段是否為空,如果為空則輸出錯誤信息并終止腳本執行,避免后續處理中因空值導致的錯誤。
格式驗證用于確保用戶輸入的數據符合特定的格式要求,比如郵箱地址、手機號碼等。對于郵箱格式驗證,通常使用正則表達式。PHP 提供了preg_match()函數來執行正則表達式匹配。郵箱的正則表達式模式一般為/^[a-zA-Z0-9._%±]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/ ,以下是驗證郵箱的代碼示例:
<?php
$email = $_POST['email']?? ''; // 使用空合并運算符確保變量存在,避免未定義索引錯誤
if (!preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $email)) {echo "郵箱格式不正確";exit;
}
// 后續處理代碼
?>
上述代碼中,preg_match()函數第一個參數是正則表達式模式,第二個參數是要驗證的郵箱地址$email。如果匹配失敗(即返回值為 0),則說明郵箱格式不正確,輸出錯誤信息并終止腳本。
手機號碼的格式驗證也類似,中國手機號碼的正則表達式模式可以是/^1[3-9]\d{9}$/,驗證代碼如下:
<?php
$phone = $_POST['phone']?? '';
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {echo "手機號碼格式不正確";exit;
}
// 后續處理代碼
?>
除了使用正則表達式,PHP 還提供了一些內置函數來輔助驗證。例如,filter_var()函數可以用于驗證和過濾各種類型的數據,包括郵箱、URL 等。使用filter_var()驗證郵箱的代碼如下:
<?php
$email = $_POST['email']?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {echo "郵箱格式不正確";exit;
}
// 后續處理代碼
?>
這里filter_var()函數第一個參數是要驗證的數據,第二個參數FILTER_VALIDATE_EMAIL表示驗證郵箱格式,如果驗證失敗則返回false 。通過這些驗證方式,可以有效地提高表單數據的準確性和可靠性,為后續的數據處理和存儲提供保障。
三、表單安全處理
3.1 防止 XSS 攻擊的方法
XSS(Cross-Site Scripting)攻擊,即跨站腳本攻擊,是一種常見的 Web 安全漏洞。攻擊者通過在網頁中注入惡意的 HTML 代碼(如 JavaScript、CSS、HTML 標簽等),當用戶瀏覽該頁面時,這些惡意代碼會被執行,從而達到攻擊用戶的目的,比如竊取用戶的 cookie、破壞頁面結構、重定向到其他惡意網站等。
XSS 攻擊主要分為三種類型:
- 反射型 XSS:這種攻擊通常是一次性的,攻擊者將惡意腳本通過 URL 參數傳遞給服務器,服務器將腳本作為響應的一部分返回,然后在用戶的瀏覽器上執行。例如,當用戶點擊一個包含惡意腳本的鏈接時,服務器將惡意腳本反射回用戶的瀏覽器并立即執行。攻擊者會構造類似這樣的惡意鏈接:https://example.com/search?q=%3Cscript%3Ealert(‘XSS’)%3C/script%3E,其中%3C和%3E分別是<和>的 URL 編碼,當用戶點擊這個鏈接,服務器返回包含惡意腳本的搜索結果頁面,瀏覽器解析頁面時就會執行alert(‘XSS’)這段腳本。
- 存儲型 XSS:惡意腳本被永久存儲在服務器上(如數據庫、消息論壇、訪客留言等),當用戶訪問相應的網頁時,存儲的腳本會被執行。比如攻擊者在論壇發布含有惡意腳本的帖子,其他用戶查看該帖子時惡意腳本被執行。攻擊者可以在論壇的留言輸入框中輸入惡意腳本<script>document.cookie=‘username=hacked;path=/’;</script>,提交后,這段腳本被存儲到服務器數據庫,當其他用戶瀏覽該留言時,腳本就會在他們的瀏覽器中執行,將用戶的username cookie 值修改為hacked。
- 基于 DOM 的 XSS:攻擊發生在用戶的瀏覽器端,通常是通過修改頁面的 DOM 環境實現的,不需要服務器端的解析和響應。例如,攻擊者利用網頁中某個可以修改 DOM 元素的 JavaScript 函數漏洞,通過構造特殊的 URL 參數,使頁面的 DOM 結構被修改并執行惡意腳本。假設網頁中有一個根據 URL 參數顯示不同內容的 JavaScript 代碼:
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>DOM - based XSS</title>
</head><body><div id="content"></div><script>var urlParams = new URLSearchParams(window.location.search);var param = urlParams.get('param');document.getElementById('content').innerHTML = param;</script>
</body></html>
攻擊者構造鏈接https://example.com/page.html?param=%3Cscript%3Ealert(‘XSS’)%3C/script%3E,用戶點擊該鏈接后,param參數的值被直接插入到div元素的innerHTML中,導致惡意腳本執行。
在 PHP 中,防止 XSS 攻擊可以采取以下措施:
- 使用 htmlspecialchars () 函數:這是一種非常常用的方法,htmlspecialchars()函數會將一些特殊字符(比如<、>、&、"、')轉換為相應的 HTML 實體,從而避免這些字符被瀏覽器解釋為 HTML 代碼。例如:
<?php
$user_input = '<script>alert("惡意腳本")</script>';
$safe_output = htmlspecialchars($user_input);
echo $safe_output;
?>
上述代碼中,$user_input中的<script>標簽會被轉換為<script>,這樣在頁面上顯示時就不會被當作 JavaScript 代碼執行,而是直接顯示文本內容<script>alert(“惡意腳本”)</script> 。
- 設置 HTTP 頭:可以通過設置X-XSS-Protection頭來啟用瀏覽器內置的 XSS 過濾機制。在 PHP 中,可以使用header()函數來設置這個頭,例如:
<?php
header('X-XSS-Protection: 1; mode=block');
?>
這表示啟用 XSS 過濾,如果檢測到 XSS 攻擊,瀏覽器將阻止渲染頁面。還可以設置Content-Security-Policy(CSP)頭,它用于指定哪些資源(如腳本、樣式表、圖片等)可以被加載和執行,從而限制了惡意腳本的執行。比如:
<?php
header("Content-Security-Policy: default-src'self'; script-src'self'");
?>
上述設置表示只允許從當前站點(self)加載所有資源,并且只允許從當前站點加載腳本,這樣可以有效防止外部惡意腳本的注入。
- 對用戶輸入進行嚴格驗證和過濾:在接收用戶輸入時,使用正則表達式等方法對輸入進行驗證,確保輸入內容符合預期格式,不包含惡意代碼。例如,對于一個接收用戶名的輸入框,只允許輸入字母、數字和下劃線,可以這樣驗證:
<?php
$username = $_POST['username']?? '';
if (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {echo "用戶名格式不正確,只能包含字母、數字和下劃線";exit;
}
// 后續處理代碼
?>
通過這種方式,將不符合要求的輸入攔截下來,避免惡意代碼進入系統 。
3.2 防止 CSRF 攻擊的措施
CSRF(Cross-Site Request Forgery)攻擊,即跨站請求偽造攻擊,是一種常見的 Web 安全漏洞。攻擊者通過誘導用戶在已登錄的網站上執行惡意操作,從而實現非法獲取用戶信息或執行操作的目的。
CSRF 攻擊的原理是利用用戶已經登錄網站的信任狀態。當用戶登錄到一個網站后,網站會在用戶的瀏覽器中設置一個會話 cookie,用于標識用戶的身份。只要用戶的瀏覽器中存在這個有效的會話 cookie,網站就會認為該用戶已經通過認證。攻擊者通過構造一個惡意的鏈接或表單,當用戶在已登錄目標網站的情況下訪問這個惡意鏈接或提交這個惡意表單時,用戶的瀏覽器會自動帶上目標網站的會話 cookie,向目標網站發送請求。由于目標網站無法區分這個請求是來自用戶的真實操作還是攻擊者的惡意偽造,就會根據請求內容執行相應的操作,從而導致 CSRF 攻擊成功。
例如,假設一個銀行網站允許用戶通過 GET 請求進行轉賬操作,轉賬的 URL 格式為https://bank.example.com/transfer?amount=100&recipient=attacker,其中amount表示轉賬金額,recipient表示收款人。攻擊者構造一個包含惡意鏈接的頁面,如:
<!DOCTYPE html>
<html><body><img src="https://bank.example.com/transfer?amount=100&recipient=attacker" alt="看似無害的圖片">
</body></html>
當用戶在登錄銀行網站后,訪問這個包含惡意鏈接的頁面,瀏覽器會自動向銀行網站發送轉賬請求,將 100 元轉賬給攻擊者,而用戶可能根本沒有意識到發生了轉賬操作。
在 PHP 中,防止 CSRF 攻擊可以采取以下措施:
- 使用 CSRF 令牌(Token):這是一種非常有效的防范方法。服務器在用戶訪問包含敏感操作(如修改密碼、轉賬等)的頁面時,生成一個唯一的、具有足夠隨機性的 CSRF 令牌。這個令牌可以通過安全的隨機數生成算法來產生,例如在 PHP 中,可以使用random_bytes()函數生成一個隨機字節序列,然后將其轉換為十六進制字符串作為 CSRF 令牌。服務器將生成的令牌存儲在用戶的會話(Session)中。在 HTML 表單中,將 CSRF 令牌嵌入到一個隱藏的表單字段中。例如:
<form action="transfer.php" method="post"><input type="hidden" name="csrf_token" value="<?php echo bin2hex(random_bytes(32));?>"><input type="text" name="amount" value=""><input type="submit" value="轉賬">
</form>
當服務器接收到表單提交請求時,從請求中提取 CSRF 令牌(從表單字段中),然后與存儲在用戶會話中的令牌進行比較。如果兩者一致,說明請求是合法的,來自用戶正常操作的頁面;如果不一致或者沒有令牌,則拒絕該請求,返回錯誤信息。在 PHP 中驗證 CSRF 令牌的代碼示例如下:
<?php
session_start();
if (!isset($_POST['csrf_token']) ||!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {echo "CSRF驗證失敗,請求可能存在風險";exit;
}
// 處理轉賬邏輯
?>
這里使用hash_equals()函數來比較令牌,它可以防止時間攻擊,確保令牌驗證的安全性。
- 驗證 Referer 字段:Referer 頭信息包含了請求的來源 URL。通過檢查這個頭信息,服務器可以大致判斷請求是否來自于合法的頁面。例如,如果一個轉賬請求的 Referer 是銀行網站自身的某個頁面,那么這個請求很可能是合法的;但如果 Referer 是一個外部的、可疑的網站,就可能是 CSRF 攻擊。在 PHP 中,可以通過$_SERVER[‘HTTP_REFERER’]來獲取 Referer 字段的值并進行驗證 。例如:
<?php
$referer = $_SERVER['HTTP_REFERER']?? '';
$allowed_domain = 'https://bank.example.com';
if (!str_starts_with($referer, $allowed_domain)) {echo "請求來源可疑,可能是CSRF攻擊";exit;
}
// 處理請求邏輯
?>
不過,需要注意的是,一些用戶可能會出于隱私原因禁用 Referer 頭信息的發送,所以不能完全依賴 Referer 檢查來防范 CSRF 攻擊。而且,攻擊者也有可能偽造 Referer 頭信息,雖然在現代瀏覽器的安全機制下,偽造有一定難度,但仍然是一個潛在風險。因此,在使用 Referer 檢查時,應該結合其他防范措施。
- 限制請求方法:限制 Web 前端頁面中表單的提交方法,如只允許 POST 請求,禁止 GET 請求。這樣,即使攻擊者誘導用戶點擊惡意鏈接,也無法通過 GET 請求執行惡意操作。因為 GET 請求的參數會暴露在 URL 中,容易被攻擊者利用來構造惡意鏈接,而 POST 請求的參數在請求體中,相對更安全。在 HTML 表單中,可以將method屬性設置為post:
<form action="sensitive_action.php" method="post"><!-- 表單內容 -->
</form>
在 PHP 中,也可以在處理請求的代碼中檢查請求方法,只處理 POST 請求:
<?php
if ($_SERVER['REQUEST_METHOD']!== 'POST') {echo "只允許POST請求";exit;
}
// 處理POST請求邏輯
?>
通過以上這些措施,可以有效地防范 CSRF 攻擊,保護用戶的信息安全和網站的正常運行。