コメントの独自スパム対策、日本語を含まない場合にスパムとして弾く方法

[WordPress] コメントの独自スパム対策、日本語を含まない場合にスパムとして弾く方法

※ 当サイトは広告を含みます。

WordPressのコメントで独自にスパム対策する方法です。コードでカスタマイズする代わりにプラグイン等は使いません。
また、スパム判定の条件も自分の好きなように改造できます。プラグイン反対派の人は参考にしてください。

管理人
管理人

スパムうぜぇぇって人は必見です。これとサーバーの機能を使えば結構な数を弾けます。

◆ 安全にfunctions.phpをカスタマイズする方法
functions.phpの修正に不安がある人は以下を参考にしてください。

コードを追加する

functions.phpとかに以下のコードを追加します。


// コメントのスパム対策
add_filter('preprocess_comment', function ($commentdata) {
  $comment_content = $commentdata['comment_content'];

  $is_spam = false;
  if (strlen(bin2hex($comment_content)) / 2 === mb_strlen($comment_content)) { // 日本語を含むか簡易チェック
    $is_spam = true;
  }

  if ($is_spam) {
    wp_die(
      '<p><strong>Error</strong>: 日本語が含まれていません。</p>',
      __('Comment Submission Failure'),
      array(
        'back_link' => true,
      )
    );
  }

  return $commentdata;
}, 2);

他の機能との干渉を考慮して優先度を2に激上げしてます。
そしてスパム判定の条件を記述してる部分は以下です。


$is_spam = false;
if (strlen(bin2hex($comment_content)) / 2 === mb_strlen($comment_content)) { // 日本語を含むか簡易チェック
  $is_spam = true;
}

最もシンプルにスパム判定できるのは日本語の有無だと思います。これは正規表現を使わなくても書けるらしい(調べたら出てきた)。
そしてスパムに悩まされてる人はご存知のように、スパムの大半が英語とか意味不明なアラビア語だったりします。
対して日本語が含まれたスパムは少ないです。あっても記事タイトルをコピペしただけとか、かなり条件が狭まります。

この記事では長さを利用して日本語を含むかどうかを判定してますが、正規表現を使えば細かい条件を追加できます。
例えば漢字やカタカナを含むとか、後は特定の文字列を含んだらNGとか。この辺は好きなように改造してください。

仕組み

コメント投稿時に利用されるfilterpreprocess_commentの中でスパム判定を行い、スパムに該当するコメントを弾きます。
さらにスパムの場合はwp_dieを呼び出して、その時点で処理を強制終了、そのままエラー画面に表示を委ねます。
filterの意味を考えると本当はwp_dieを呼び出すのは微妙なんですが、後述する解説の理由から選択肢がありません。

WordPressのコメント処理

このコードを作るためにWordPressのソースを適当に読みました。とりあえず、現在の[バージョン6.4.1]では以下の処理になってます。

まず、コメントを書き込むとwp-comments-post.phpに飛びます。これは表示されるURLを見れば分かると思います。
次にwp-comments-post.phpではwp_handle_comment_submission()関数が呼ばれます。

この関数は大雑把にこんな感じになってます。


function wp_handle_comment_submission($comment_data)
{
  //
  // 記事のステータスとかログイン状態を確認したり色々(結構長い)。
  // 恐らく今回の主題とは関係ないから無視していい。
  //

  $allow_empty_comment = apply_filters('allow_empty_comment', false, $commentdata);
  if ('' === $comment_content && !$allow_empty_comment) {
    return new WP_Error('require_valid_comment', __('<strong>Error:</strong> Please type your comment text.'), 200);
  }

  $check_max_lengths = wp_check_comment_data_max_lengths($commentdata);
  if (is_wp_error($check_max_lengths)) {
    return $check_max_lengths;
  }

  $comment_id = wp_new_comment(wp_slash($commentdata), true);
  if (is_wp_error($comment_id)) {
    return $comment_id;
  }

  //
  // 省略
  //
}

そして、この関数内にwp_new_comment()って関数があり、この中にコメントを書き込むための実処理が入ってます。
正確には内側でwp_insert_comment()を呼び出し、その中で$wpdb->insert()を実行、その時点でDBにコメントが追加されます。


function wp_insert_comment($commentdata)
{
  //
  // 省略
  //

  if (!$wpdb->insert($wpdb->comments, $compacted)) {
    return false;
  }

  //
  // 省略
  //
}

つまり、この$wpdb->insert()が呼び出されるより前にスパムを判定して弾く必要があります。
では、その弾く処理をどこで実現するかですが、ここで使えそうなactionまたはfilterを探します。

ちなみにwp_insert_comment()にはactionfilterが無いので諦め、呼び出し元のwp_new_comment()に戻ります。
wp_new_comment()の中身はこんな感じです。wp_insert_comment()より手前は、$commentdataに書き込み用データを追加してます。


function wp_new_comment($commentdata, $wp_error = false)
{
  //
  // 省略
  //
  $commentdata = apply_filters('preprocess_comment', $commentdata);

  //
  // 省略
  //
  $comment_id = wp_insert_comment($commentdata);

  //
  // 省略
  //
}

でっ、ここに存在するのがpreprocess_commentです。と言うか、これしかありません。

さらに前に戻るとwp_handle_comment_submission()の中にallow_empty_commentってfilterが存在します。
これは空のコメントを許可するかを意味するため、ここで処理するのは、より微妙な気持ちになります。

さらにさらに戻ることで出てくるのがpre_comment_on_postです。なんとこちらactionになります。
おっ、これ使えるじゃんと思うのですが、呼び出し時に得られる引数が$comment_post_idのため、正攻法ではコメントの内容が得られません。

管理人
管理人

そんな事情から1番まともそうな'preprocess_comment'を選びました。

呼び出し順-> wp_handle_comment_submission();
  -> apply_filters( 'allow_empty_comment' );
    -> wp_new_comment();
      -> apply_filters( 'preprocess_comment' );
        -> wp_insert_comment();

あとがき

エックスサーバーだとサーバー側にもスパム対策があって、そこで国外からのコメントを弾いてます。
他にも大量トラフィック除外とかあって、それらの機能と本記事の改造でスパムが激減しました。

管理人
管理人

この記事アップして日本語のスパム増えたら、暇なんだなって感心しちゃうね。

りさ
りさ

どうしても国外からコメントしたい人は管理人のSNSに直接メッセージを送ってね。

この記事は参考になりましたか?

関連記事

コメント

この記事へのコメントはありません。