系列第三部分實作後台管理頁 manage.php:密碼驗證、列出所有留言附核取方塊、批次刪除、寫回文字檔。
$password (GET/POST) 驗證是否等於 $managepwd,失敗導回首頁。$dispflag == "show" 時,讀取檔案逐行輸出,每行前加核取方塊 <input type=checkbox name=check$h value=$h>。$submit 送出時,再次驗證密碼,迴圈檢查 check1~checkN,將對應陣列元素設為空字串,過濾後寫回檔案。<?php
include "sys.php"; // 定義 $managepwd, $guestfile
if ($password != $managepwd && $dispflag) {
echo "<meta http-equiv=Refresh content=5;url=guest.php>";
echo "<center><font color=red>密碼錯誤!無法刪除留言!</font></center>";
exit;
}
?>
<?php
if ($dispflag == "show") {
$content = file($guestfile);
$count = count($content);
for ($h = $count; $h > 0; $h--) {
echo "<tr><td>刪除第 $h 條留言:<input type=checkbox name=check$h value=$h></td></tr>\n\n";
echo $content[$h-1];
}
}
?>
<?php
if ($submit) {
if ($password != $managepwd) { /* 錯誤處理 */ exit; }
$guest_content = file($guestfile);
$count = count($guest_content);
for ($j = 1; $j <= $count; $j++) {
$var = "check$j";
$del_num = $$var; // 變數變數取得 check1, check2...
if ($del_num) {
$guest_content[$del_num - 1] = "";
}
}
$fp = fopen($guestfile, "w");
for ($i = 0; $i <= $count - 1; $i++) {
if ($guest_content[$i] != "") {
fputs($fp, $guest_content[$i], strlen($guest_content[$i]));
}
}
fclose($fp);
echo "<meta http-equiv=Refresh content=5;url=guest.php>";
echo "<center><font color=red>留言已正確刪除</font></center>";
exit;
}
?>
| 問題 | 風險 | 改進 |
|---|---|---|
| 密碼明文傳輸、明文比對、無 HTTPS | 竊聽即得管理權 | 強制 HTTPS、密碼雜湊驗證 (password_verify) |
變數變數 $$var + 直接使用 $_GET/$_POST 全域 | 變數注入、難追蹤、PHP 8.2 棄用 | 陣列遍歷 foreach ($_POST['checks'] as $id) |
| 無檔案鎖定 | 併發刪除導致資料損毀 | flock() 或改用資料庫交易 |
| 無 CSRF Token | 偽造刪除請求 | 表單加入 <input type=hidden name=csrf value=...> |
| 無操作日誌 | 無法追蹤誰在何時刪除何留言 | 記錄管理員 ID、時間、刪除留言 ID 至日誌表 |
| HTML 輸出未轉義 | 留言內容含惡意 Script 將在管理頁執行 | htmlspecialchars() 所有動態輸出 |
messages(id, name, email, content, ip, created_at, deleted_at),軟刪除 UPDATE ... SET deleted_at=NOW() WHERE id IN (...)。DELETE /api/admin/messages/{id},後端驗證 JWT/Session + Policy 授權。admin_logs(admin_id, action, target_id, ip, ua, created_at)。