0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

不用外掛,替 WordPress 登入頁和留言表單加簡單驗證題

不用外掛,替 WordPress 登入頁和留言表單加簡單驗證題 封面

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,等於把答案直接交給它。

這裡的做法是:

  1. 出題時,把「答案+出題時間」用 WordPress 的 wp_hash() 算成一串雜湊值,和出題時間一起放在隱藏欄位。雜湊值是用網站 wp-config.php 裡的密鑰算的,從雜湊值反推不出答案,別人也沒辦法自己偽造一組。
  2. 送出時,用訪客填的答案和同一個時間再算一次,兩串一樣才算答對。比對用 hash_equals(),避免從比對花的時間猜出答案。
  3. 超過一天的題目不算數。機器人沒辦法拿很久以前的題目一直重複用。

這種做法不用存資料庫,也不受網站快取影響:整頁快取的頁面就算一直顯示同一題,只要在一天之內都能答。

各段程式碼在做什麼

  • 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 擋下來。

這種驗證題擋得住什麼、擋不住什麼

  • 擋得住:大量亂槍打鳥的機器人。它們通常只會照固定格式送出帳號密碼或留言,不會讀網頁上的題目。
  • 擋不住:專門針對你網站寫的程式。一位數加法很容易被程式解出來,同一題在一天內也能重複使用。
  • 擋不住真人。真的有人手動貼廣告留言,還是要靠留言審核。

所以它是「第一道門」,搭配其他做法效果最好:

使用時要注意

  • WooCommerce「我的帳號」的登入、註冊表單不是 wp-login.php,這段程式碼沒有處理,維持原本的樣子。
  • 有些安全外掛、登入外掛會換掉登入頁,題目可能不會出現。加了之後,先開無痕視窗試登入一次,確定自己進得去再關掉後台。
  • 萬一真的鎖在門外,用 FTP 或主機的檔案管理員,把放這段程式碼的檔案改名或拿掉這段,就能恢復。

延伸閱讀

網站的安全,靠的是好幾道門一起顧。正威網路行銷是 25 年經驗的基隆網頁設計工作室,網站管家年費包含系統、外掛與安全性更新和每日備份,台北與全台客戶也歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

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

讓我們一起打造
您的專屬網站!