
WordPress 網站一上線,登入頁每天都有機器人在猜帳號密碼,留言區也常被灌一堆廣告連結。安裝驗證碼外掛是一種方法,但如果只想擋掉最常見、最無腦的那種機器人,自己加一題簡單的加法就夠了:
驗證題:3 加 4 等於多少?(請填數字)
這篇的程式碼會在登入、註冊、留言三個表單加上這一題,答錯就擋下來。放在子佈景主題的 functions.php,或用 Code Snippets 這類外掛新增。
程式碼
PHP
// 產生一題加法:答案不放在網頁上,只放「答案+時間」算出來的雜湊值
function zw_cap_new() {
$a = wp_rand( 1, 9 );
$b = wp_rand( 1, 9 );
$time = time();
return array(
'question' => "{$a} 加 {$b} 等於多少?",
'token' => $time . '-' . wp_hash( ( $a + $b ) . '|' . $time, 'nonce' ),
);
}
// 檢查答案:雜湊值要對得上,而且題目是一天之內產生的
function zw_cap_check() {
$token = sanitize_text_field( wp_unslash( $_POST['zw_cap_token'] ?? '' ) );
$answer = trim( sanitize_text_field( wp_unslash( $_POST['zw_cap_answer'] ?? '' ) ) );
$parts = explode( '-', $token, 2 );
if ( 2 !== count( $parts ) || ! ctype_digit( $parts[0] ) || ! ctype_digit( $answer ) ) {
return false;
}
if ( time() - (int) $parts[0] > DAY_IN_SECONDS ) {
return false;
}
return hash_equals( wp_hash( (int) $answer . '|' . $parts[0], 'nonce' ), $parts[1] );
}
// 在登入、註冊、留言表單顯示題目(已登入的人留言時不會出現)
function zw_cap_field() {
$cap = zw_cap_new();
echo '<p class="zw-captcha">'
. '<label for="zw_cap_answer">驗證題:' . esc_html( $cap['question'] ) . '(請填數字)</label>'
. '<input type="text" name="zw_cap_answer" id="zw_cap_answer" class="input" inputmode="numeric" autocomplete="off" required>'
. '<input type="hidden" name="zw_cap_token" value="' . esc_attr( $cap['token'] ) . '">'
. '</p>';
}
add_action( 'login_form', 'zw_cap_field' );
add_action( 'register_form', 'zw_cap_field' );
add_action( 'comment_form_after_fields', 'zw_cap_field' );
// 登入:答錯就擋下來(不管帳號密碼對不對)
add_filter( 'authenticate', function ( $user ) {
if ( 'wp-login.php' !== ( $GLOBALS['pagenow'] ?? '' ) || ! isset( $_POST['log'] ) ) {
return $user;
}
if ( ! zw_cap_check() ) {
return new WP_Error( 'zw_captcha', '<strong>錯誤</strong>:驗證題答錯了,請再試一次。' );
}
return $user;
}, 30 );
// 註冊:答錯就不建立帳號
add_filter( 'registration_errors', function ( $errors ) {
if ( ! zw_cap_check() ) {
$errors->add( 'zw_captcha', '<strong>錯誤</strong>:驗證題答錯了,請再試一次。' );
}
return $errors;
} );
// 留言:沒登入的訪客才檢查
add_filter( 'preprocess_comment', function ( $commentdata ) {
if ( is_user_logged_in() || ! in_array( $commentdata['comment_type'] ?? '', array( '', 'comment' ), true ) ) {
return $commentdata;
}
if ( ! zw_cap_check() ) {
wp_die( '驗證題答錯了,請回上一頁再試一次。', '留言沒有送出', array( 'response' => 403, 'back_link' => true ) );
}
return $commentdata;
} );答案為什麼不放在網頁上
網路上很多舊教學,會把正確答案放在表單的隱藏欄位,或存在 Cookie 裡,送出時拿來比對。問題是機器人讀得到網頁原始碼和 Cookie,等於把答案直接交給它。
這裡的做法是:
- 出題時,把「答案+出題時間」用 WordPress 的
wp_hash()算成一串雜湊值,和出題時間一起放在隱藏欄位。雜湊值是用網站wp-config.php裡的密鑰算的,從雜湊值反推不出答案,別人也沒辦法自己偽造一組。 - 送出時,用訪客填的答案和同一個時間再算一次,兩串一樣才算答對。比對用
hash_equals(),避免從比對花的時間猜出答案。 - 超過一天的題目不算數。機器人沒辦法拿很久以前的題目一直重複用。
這種做法不用存資料庫,也不受網站快取影響:整頁快取的頁面就算一直顯示同一題,只要在一天之內都能答。
各段程式碼在做什麼
zw_cap_field():印出題目和答案欄。掛在login_form(登入)、register_form(註冊)、comment_form_after_fields(留言)。最後這個只有「沒登入的訪客」留言時才會出現,已登入的會員不用答題。- 登入的檢查掛在
authenticate,優先順序 30,排在 WordPress 檢查帳號密碼之後。答錯時不管帳號密碼對不對,一律回傳「驗證題答錯了」。這樣機器人就算猜中密碼,也看不出來,沒辦法一直試。 - 只檢查從
wp-login.php登入表單送出的資料。WordPress 的 App 密碼、XML-RPC 這類程式用的登入方式不受影響,網站接的 App 或外部服務不會因此壞掉。 - 註冊用
registration_errors,答錯就不建立帳號。 - 留言用
preprocess_comment,答錯顯示「驗證題答錯了,請回上一頁再試一次」,回應碼 403。只檢查一般留言,Pingback、Trackback 和 WooCommerce 的商品評價不受影響。
實際測試的結果
登入頁
- 驗證題空白、帳號密碼正確:擋下來,顯示「驗證題答錯了」。
- 驗證題答錯、帳號密碼正確:擋下來。
- 驗證題答對、密碼錯:顯示一般的「密碼不正確」。
- 驗證題答對、密碼正確:登入成功。
- 模擬機器人,不經過網頁、直接把正確的帳號密碼送到
wp-login.php:顯示「驗證題答錯了」,沒有登入。
註冊:答錯不建立帳號;答對正常註冊。
留言(Twenty Twenty-Five、Twenty Twenty-One 都測過):答錯顯示提示並回應 403;答對留言送出,進入待審核。
直接送資料到留言程式:沒有驗證碼、竄改過的驗證碼、超過一天的驗證碼,都回應 403 擋下來。
這種驗證題擋得住什麼、擋不住什麼
- 擋得住:大量亂槍打鳥的機器人。它們通常只會照固定格式送出帳號密碼或留言,不會讀網頁上的題目。
- 擋不住:專門針對你網站寫的程式。一位數加法很容易被程式解出來,同一題在一天內也能重複使用。
- 擋不住真人。真的有人手動貼廣告留言,還是要靠留言審核。
所以它是「第一道門」,搭配其他做法效果最好:
- 登入保護:兩步驟驗證、限制登入次數,請看WordPress 登入安全怎麼做。
- 留言:搭配隱藏欄位(蜜罐)、留言審核,請看WordPress 防垃圾留言總整理。
- 被打得很兇的網站,建議換成 Cloudflare Turnstile、Google reCAPTCHA 這類專業的驗證服務。
使用時要注意
- WooCommerce「我的帳號」的登入、註冊表單不是
wp-login.php,這段程式碼沒有處理,維持原本的樣子。 - 有些安全外掛、登入外掛會換掉登入頁,題目可能不會出現。加了之後,先開無痕視窗試登入一次,確定自己進得去再關掉後台。
- 萬一真的鎖在門外,用 FTP 或主機的檔案管理員,把放這段程式碼的檔案改名或拿掉這段,就能恢復。
延伸閱讀
- WordPress 登入安全怎麼做?兩步驟驗證、限制登入次數與修改登入網址
- WordPress 防垃圾留言總整理:外掛、留言設定、蜜罐與字數限制
- WordPress 限制每篇文章只能留言一次:用程式碼防止重複留言
網站的安全,靠的是好幾道門一起顧。正威網路行銷是 25 年經驗的基隆網頁設計工作室,網站管家年費包含系統、外掛與安全性更新和每日備份,台北與全台客戶也歡迎來聊聊。







發表留言
還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。