スパム対応その1
たいしてアクセスのないサイトだが、スパムコメントが来るようになった。挙動を観察するに、特定のページにだけきているので、何らかの検索ワードに対しリンクを貼るだけの価値が出てしまっているのかもしれない。それだけの理由でもコメントとして尊重すべきという考えもあるかもしれないが、何も述べていないに等しい非常に抽象的なコメントに、明らかにリンクを集めるのが目的のURLが貼られていると、やはりスパムだと思ってしまう。
まずは軽い対応からしようと思う。コメントは投稿フォームのaction
属性のPHPスクリプトにcurl等で直接ポストすることができ、スパムコメントはそのような手法がとられることが多い。そこでデフォルトのスクリプト名(wp-comments-post.php)を変えてしまうというのがある。しかしバージョンアップ時のルーチン作業が発生するのは良くない(極力自動でアップデートしたい)ので、デフォルトURLはアクセス禁止にした上で、プラグインで代わりの投稿先を用意し、wp-comments-post.php はそこでrequire
で読み込む。まずは、.htaccessでアクセスをふさぐ。
<Files "wp-comments-post.php">
Require all denied
</Files>
プラグインのエントリーポイントでは、jsファイルの挿入と、コメントフォーム生成時のフィルターフックの登録。ページがロードされた時点ではform.action
は設定せず、投稿ボタンクリック時に動的に設定する。
<?php
/*
Plugin Name: imnt-robot
Plugin URI: https://osmatsuda-b.sakura.tv
Description: Plugin for shadowing ‘wp-comments-post.php’
Author: osmatsuda@gmail.com
Version: 0.0.1
Author URI: https://osmatsuda-b.sakura.tv
*/
define('IMNT_ROBOT_DIR_URL', plugin_dir_url(__FILE__));
$imnt_robot_comment_form_action_original = '';
function imnt_robot_comment_add_fields($post_id) {
global $imnt_robot_comment_form_action_original;
echo "<input type='hidden' name='imnt_robot_default_action' value='$imnt_robot_comment_form_action_original' />
<input type='hidden' name='imnt_robot_dir_url' value='".IMNT_ROBOT_DIR_URL."' />
";
}
function imnt_robot_set_comment_form_action($defaults) {
global $imnt_robot_comment_form_action_original;
$imnt_robot_comment_form_action_original = $defaults['action'];
$defaults['action'] = '';
return $defaults;
}
function imnt_robot_enqueue_scripts() {
if (is_single()) {
wp_enqueue_script('imnt_robot_script', IMNT_ROBOT_DIR_URL.'imnt-robot.js');
}
}
add_action('wp_enqueue_scripts', 'imnt_robot_enqueue_scripts');
add_filter('comment_form_defaults', 'imnt_robot_set_comment_form_action');
add_action('comment_form', 'imnt_robot_comment_add_fields');
document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('form#commentform');
form.submit.addEventListener('click', (e) => {
e.preventDefault();
const input = form.querySelector('input[name=imnt_robot_dir_url]');
if (input) {
form.action = input.value + 'imnt-robot-comments-post.php';
form.submit.click();
}
}, { once: true });
});
最後に投稿先のスクリプトでは、リファラとパラメータを確認したのち元のwp-comments-post.phpをロードしている。
<?php
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] &&
isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] &&
isset($_POST['imnt_robot_default_action']) && $_POST['imnt_robot_default_action']) {
$prefixlen = strlen($_SERVER['HTTP_ORIGIN']);
if (!str_contains($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_ORIGIN']) ||
!str_contains($_POST['imnt_robot_default_action'], $_SERVER['HTTP_ORIGIN'])) {
header('HTTP/1.1 403 Forbidden');
exit(0);
}
$ref_url_comps = explode('/', substr($_SERVER['HTTP_REFERER'], $prefixlen));
$dfa_url_comps = explode('/', substr($_POST['imnt_robot_default_action'], $prefixlen));
foreach ($ref_url_comps as $i => $comp) {
if ($comp === $dfa_url_comps[$i])
continue;
break;
}
$wphome = $_SERVER['DOCUMENT_ROOT'].implode('/', array_slice($ref_url_comps, 0, $i));
$dfa = $wphome.'/'.implode('/', array_slice($dfa_url_comps, $i));
if (is_file($dfa)) {
require $dfa;
} else {
header('HTTP/1.1 403 Forbidden');
exit(0);
}
} else {
header('HTTP/1.1 403 Forbidden');
exit(0);
}
第一段階はここまで。しかし、ページを経由しないコメントの直接投稿を阻止しているだけなので、WebDriverを使ったスパムの自動化投稿には対応できない。次の段階では、ロボットではないことを判断できる仕組みを導入したいが、結局イタチごっこになるので、最終的には公開から一定期間を過ぎたページにはコメントできないようにすると思う。