本文剖析一套 2000 年代典型的「仿 OSO 風格」PHP 論壇原始碼第三部分 readforum.php,展示早期 PHP 直連 MySQL、註冊全域變量、SQL 拼接、HTML/JS 混寫的典型寫法,並逐一標註安全隱患與現代重構方向。
readforum.php
├── HTML 骨架(HEAD、CSS、Meta)
├── include("c:mydbheader.inc") // 絕對路徑包含資料庫設定
├── 邏輯區()
│ ├── 連接 MySQL (mysql_connect + mysql_select_db)
│ ├── 參數處理 ($readflag, $theme_id, $username 等)
│ ├── 發文邏輯 (INSERT fr_t_forumtitle + fr_t_forumcontent)
│ ├── 閱讀計數/回覆計數更新 (UPDATE)
│ └── 查詢主題標題/計數 (SELECT)
└── 表現區(HTML + 內嵌 PHP 印出變數)
用戶請求 readforum.php?readflag=123
↓
$readflag > 0 ? $theme_id = $readflag : 新增主題流程
↓
新增主題:INSERT fr_t_forumtitle (id, state, readcount, replycount, title, createman, replytime)
↓
插入內容:INSERT fr_t_forumcontent (id, content, replyman, replyemail, replyhttp, replytime, replyface)
↓
更新統計:UPDATE fr_t_forumtitle SET readcount+1, replycount+1, replytime=NOW(), replyman=...
↓
查詢顯示:SELECT title, readcount, replycount FROM fr_t_forumtitle WHERE id=$theme_id
↓
輸出 HTML:主題標題、閱讀數、回覆數、發新貼/回覆/列表按鈕
| 隱患類型 | 代碼位置 | 風險等級 | 攻擊向量 | 修正方案 |
|---|---|---|---|---|
| SQL 注入 | 全部 mysql_query($tempstr) 直接拼接 $forumtitle、$username、$forumcontent 等 | 🔴 致命 | title=test', '0',0,-1,'hacked','attacker',NOW());-- | 改用 PDO Prepared Statements:$stmt=$pdo->prepare('INSERT ... VALUES (?,?,?)'); $stmt->execute([$id,$title,$user]); |
| XSS (儲存型/反射型) | print $ls_theme_title、echo $forumcontent 直接輸出未過濾 | 🔴 致命 | 發文內容含 <script>stealCookie()</script> | 輸出時 htmlspecialchars($str, ENT_QUOTES, 'UTF-8');入庫可選 HTMLPurifier 白名單過濾 |
| 註冊全域變量依賴 | 直接使用 $username、$forumtitle 等未定義變量 | 🟠 高 | register_globals=On 時任意變量覆蓋 | 強制 register_globals=Off,統一從 $_GET/$_POST/$_REQUEST 取值並驗證 |
| 硬編碼資料庫憑證 | mysql_connect('localhost:3306','root','') | 🟠 高 | 原始碼洩露即資料庫失陷 | 配置檔置於 Web 根目錄外,使用環境變數或 .env 載入 |
| 絕對路徑包含 | include("c:mydbheader.inc") | 🟡 中 | 遷移部署失敗、路徑遍歷風險 | 改用相對路徑 __DIR__.'/config/db.php' 或 Composer 自動載入 |
| 已棄用擴展 | mysql_* 函數族 (PHP 7.0 移除) | 🔴 致命 | 無法在 PHP 5.6+ 運行 | 全面遷移至 PDO 或 mysqli |
| 無 CSRF 保護 | 發文表單無 Token | 🟡 中 | 誘導用戶訪問惡構造頁面自動發文 | 表單加入 <input type="hidden" name="csrf" value="= $_SESSION['csrf'] ?>"> |
| 無輸入長度/格式驗證 | 標題、內容直接入庫 | 🟡 中 | 超長字串撐爆 VARCHAR、儲存型 XSS | 前後端雙重驗證:mb_strlen、strip_tags、正則白名單 |
| 錯誤資訊洩露 | die("寫方式打開檔案 $filename 失敗") 等 | 🟢 低 | 路徑、SQL 結構外洩 | 生產環境 display_errors=Off,統一錯誤頁面,記錄詳細日誌 |
CREATE TABLE fr_t_forumtitle (
id INT PRIMARY KEY AUTO_INCREMENT,
state TINYINT DEFAULT 0,
readcount INT DEFAULT 0,
replycount INT DEFAULT -1, -- 首貼不算回覆
title VARCHAR(255),
createman VARCHAR(50),
replytime DATETIME
);
CREATE TABLE fr_t_forumcontent (
id INT, -- 關聯 fr_t_forumtitle.id
content TEXT,
replyman VARCHAR(50),
replyemail VARCHAR(100),
replyhttp VARCHAR(255),
replytime DATETIME,
replyface TINYINT -- 表情/頭像代碼
);
ForumRepository。respect/validation 或 Laravel Validator 定義規則集。e() 助手函數。_token。$username。LIMIT/OFFSET 或游標分頁,避免全表掃描。這段代碼是 PHP 早期「人人都能寫論壇」時代的縮影:功能優先、安全缺位、耦合極高。它提醒我們:
若您正在維護類似舊專案,建議採取「絞殺者模式」:在新框架中逐模塊重寫,以 API 代理舊系統,漸進式替換,而非大爆炸重寫。