なぜ既存プラグインがあるのに自分たちでスパム対策を作ったのか
始まりは、月曜の朝だった。始業と同時に電話が鳴った。
「週末の間にお問い合わせフォームから600件のスパムが来ました。また、です。」
大量のスパムにクライアントはフォームそのものへの信頼を失いつつあった。仮想通貨やSEOサービスなど、ボットがその週売り込んでいるゴミ投稿の山に、本物のリードが埋もれていた。
これは CF7 Ultimate Honeypot の開発ストーリーだ。なぜこのプラグインが生まれたのか、書く前に何を試したのか、そしてどんな技術的判断がこれを形作ったのか。スパムで溢れた受信箱を見つめながら「なぜどの”ソリューション”も本当に解決してくれないのか」と思ったことがある人は、ぜひ読んでほしい。
私たちが直面した問題
私たちはWordPressサイトの構築と保守を仕事にしている。数十サイトを運用し、そのほぼ全てで Contact Form 7 を使っている。軽量で柔軟、コンタクトフォームがやるべきことをマーケティングプラットフォームになろうとせずにやってくれるからだ。
しかし CF7 にはスパム対策が内蔵されていない。これは意図的な設計判断だ。作者の Takayuki Miyoshi 氏はプラグインをスリムに保ち、それ以外はエコシステムに任せている。私たちはその姿勢を尊重する。ただ、それは全ての CF7 サイトが何かをボルト留めするまで無防備ということでもある。
スパムはもう昔の姿をしていない
2012年の「バイアグラ買いませんか」メッセージの話ではない。2024年から2025年にかけて私たちのクライアントのフォームを攻撃していたボットは、ヘッドレスブラウザを動かしていた。JavaScriptを実行し、CSSをレンダリングし、人間のようにフォームを入力する——キーストローク間のタイミングまでリアルに再現して。中には、大規模言語モデルで生成されたと思われる、本物のビジネス問い合わせにしか見えないメッセージまであった。
コンテンツフィルタでは捕まえられない。文法的に正しく、文脈的にも適切。クライアントの業界に言及し、価格について質問してくる。唯一の手がかりは件数だ。同じ「人物」が火曜日の午後に40の異なる企業に興味を持っているらしい。
被害は単なる迷惑では済まなかった
お問い合わせフォームのスパムは、個人メールのスパムとは訳が違う。ビジネスオーナーが受信箱を開いて200件のゴミメッセージと5件の本物のメッセージが混在しているのを見たとき、丁寧に仕分けたりしない。受信箱を見なくなるのだ。私たちは、S/N比の崩壊によってクライアントが本物の問い合わせを見逃すのを目の当たりにした。
あるクライアントは、フォーム経由の問い合わせの確認を完全にやめたと言った。電話と来店だけに頼っていた。私たちが作り、彼らがお金を払ったWebサイトのお問い合わせページは、事実上死んでいた。
それが、この問題が「ちょっとした迷惑」から「ちゃんと解決すべき課題」に変わった瞬間だった。
試したこと
いきなりコードを書き始めたわけではない。私たちは良いソリューションが既にあるのに自前で作るタイプの開発者ではない。WordPressエコシステムが提供するあらゆるものを数ヶ月かけてテストした。正直な結果を報告する。
日本クイズ
月曜の朝に電話をかけてきたクライアントは日本企業だった。正当な問い合わせのほぼ全てが日本国内からのもの。一方、スパムの99%は海外からの送信だった——外国語のメッセージ、海外のIPアドレス、何もかもが国外。
そこでシンプルなことを試した。「日本で生活していれば答えられるクイズ」をフォームに追加したのだ。たとえば「日本の郵便ポストの色は?」とか、コンビニチェーンに関する質問とか。CAPTCHAも画像グリッドもない——ローカルな文化的知識を問うテキストフィールドを一つ置いただけだ。
効果は劇的だった。スパムは一夜にしてほぼゼロになった。そのクライアントに関しては、問題は解決した。
しかし長くは喜べなかった。このアプローチには2つの深刻な欠陥があった。
第一に、フォームのデザインが台無しになった。プロフェッショナルなお問い合わせフォームの途中にランダムなトリビア質問が挟まっている。違和感しかない。クライアントのマーケティングチームから反発があった。何ヶ月もかけてページのUXを磨いてきたのに、郵便ポストの色を聞く質問がフローを壊している。スパム対策のせいでフォームの見た目が悪くなるのは本末転倒だ。
第二に、この方法には有効期限があると分かっていた。クイズが効いたのは、2024年のボットが答えをググるほど賢くなかったからだ。しかしLLMが月単位で進化している今、「日本の新幹線の名前は?」にボットが人間と同じくらい簡単に答えられるようになるまで、どれだけかかるだろう? 1年、もって2年。AIボットが文化的知識の質問を突破するのは時間の問題だった。巧みなハックではあったが、持続可能なソリューションではなかった。
必要だったのは、ユーザーに見えず、デザインを損なわず、ボットが賢くなった瞬間に陳腐化しないもの。この認識が、より広いプラグインエコシステムの調査へと私たちを駆り立てた。
Akismet
Akismet はWordPressにおけるスパム対策のデフォルト回答だ。Automattic製で、プリインストールされていて、巨大なスパムデータベースを持っている。私たちも何年も使っていた。
問題はボットが賢くなってから始まった。Akismet はコンテンツ分析で動く——投稿テキストを既知のスパムパターンと照合する。「安い時計買いませんか」メッセージには見事に効く。しかし、LLMが投稿ごとにユニークで文脈に合ったメッセージを生成するようになると、通用しない。
GDPRの問題も発生した。Akismet はフォーム送信データを分析のためにアメリカの外部サーバーに送信する。EUベースのクライアントにとって、これはコンプライアンス上の頭痛の種だった。同意通知を追加し、プライバシーポリシーを更新し、非技術者のビジネスオーナーに「なぜ訪問者のメッセージがサードパーティAPIを経由するのか」を説明しなければならなかった。一部のクライアントは単純に拒否した。
そしてコスト。Akismet は個人サイトなら無料だが、商用利用には有料プランが必要だ。それ自体は妥当だ。しかし、スパムの半分を通してしまうフィルタに毎月お金を払うのは間違っていると感じた。
Google reCAPTCHA
最初に reCAPTCHA v2 を試した。「私はロボットではありません」チェックボックス。技術的には機能した。スパムは減った。しかしコンバージョンも減った。あるクライアントのサイトで、導入初月にフォーム完了率が22%低下した。モバイルはもっと酷く、画像グリッドの選択に苛立って離脱するユーザーが続出した。
reCAPTCHA v3(見えないバージョン)に切り替えた。可視チャレンジがないのでUXは改善。しかし v3 は v3 で別の問題をもたらした:
- パフォーマンスへの影響。 GoogleのJavaScriptペイロードがページ読み込みに200〜400ms追加。Core Web Vitalsの最適化に何週間もかけたサイトにとって、これは後退だった。
- 誤検知。 VPN、Tor、プライバシー重視のブラウザを使うユーザーが一貫して低スコアになりブロックされた。クライアントが本当にリーチしたいプライバシー意識の高いユーザーを、私たちが罰していることになる。
- 再びGDPR問題。 reCAPTCHA は行動テレメトリーをGoogleに送信する。複数の欧州データ保護当局がこれを問題視している。同じコンプライアンスの議論に逆戻りだ。
- それでもスパムは通った。 ボットオペレーターはフォームを叩く前にブラウザセッションを「ウォームアップ」して高い信頼スコアを積む方法を学んだ。この軍拡競争はGoogleの代わりに私たちが勝てるものではなかった。
基本的なハニーポットプラグイン
WordPress公式ディレクトリで CF7 用のハニーポットプラグインをいくつか見つけた。コンセプトは健全だ。人間には見えないがボットが入力してしまう隠しフィールドを追加する。送信時にフィールドにデータがあれば、静かに拒否する。
いくつかインストールした。約2週間は機能した。
問題は、これらのプラグインが静的で予測可能な実装だったことだ。隠しフィールドの名前は毎回同じ。CSSの隠し方も常に display: none か visibility: hidden。一度フォームに遭遇したボットはパターンを学習し、以後永久にハニーポットフィールドをスキップできる。
そして実際にそうなった。Puppeteerを動かす第2世代ボットは入力前にcomputed styleをチェックする。display: none の input は自明に検出可能だ。基本的なハニーポットは「スパムの90%を捕捉」から「30%を捕捉」へ、ボットスクリプトの更新に伴い数ヶ月で落ちていった。
複数プラグインの組み合わせ
ある時期、一つのサイトで Akismet、reCAPTCHA v3、ハニーポットプラグイン、レート制限プラグインを同時に動かしていた。4つのプラグイン。4つの潜在的障害点。4つの更新・設定管理対象。
スパムは大幅に減った。しかし保守の負担が馬鹿げていた。プラグイン同士が競合する。reCAPTCHA がページキャッシュと干渉する。Akismet の API がたまにタイムアウトしてフォーム送信を止める。レート制限が、最初の送信が通ったか不安で2回目を送った正当なユーザーをブロックする。
スパム問題を解決するために信頼性の問題を作っていた。これはエンジニアリングではない。ガムテープだ。
何を作ったか
私たちは腰を据えてシンプルな問いを立てた。理想のスパム対策は具体的にどういうものか?
要件リストを書き出した。「機能」ではなく「要件」だ。
- UIなし。 ユーザーは保護の存在を知るべきではない。チェックボックスもパズルもバッジもなし。
- 外部依存なし。 Google、Automattic、その他への API コールなし。サイトがホストされているサーバー上で全て完結。
- デフォルトでGDPR準拠。 Cookie なし。サードパーティへのデータ転送なし。スパムフィルタのために同意バナーは不要。
- 軽量。 300KB の JavaScript ペイロードなし。ページ読み込み時間や Core Web Vitals への測定可能な影響なし。
- ボットの進化に耐性あり。 ボットがスクリプトを更新したら壊れる静的防御ではなく、自分自身の形を変えるもの。
- ページキャッシュと互換。 WordPress の nonce はキャッシュされたページでは壊れる。私たちのソリューションはそれに依存できない。
- 設定不要。 インストール、有効化、以上。APIキーも閾値調整もフォームごとの設定もなし。
このリストが CF7 Ultimate Honeypot の設計仕様になった。
核心のアイデア:ポリモーフィック防御
既存のハニーポットプラグインの最大の失敗は予測可能性だった。同じフィールド名、同じ隠し方、DOMの同じ位置。パターンを一度解読したボットは永久にスキップできる。
フォームが読み込むたびに違って見える必要があった。ユーザーにとっての違いではない——ユーザーには何も見えない。HTMLを解析するボットにとっての違いだ。
ポリモーフィック・ハニーポットは、ページを読み込むたびにフィールド名をローテーションする。隠しフィールドの名前は honeypot でも hp_field でもない。動的に生成された、DOMをスキャンするボットには正規のフォームフィールドに見えるものだ。CSSの隠し手法もローテーションする——画面外への配置だったり、overflow: hidden のゼロ高コンテナだったり、複数テクニックの組み合わせだったり。ボットは私たちのハニーポットをスキップする静的ルールを作れない。ハニーポットが二度と同じ姿にならないからだ。
多層バリデーション
単一の検出手法では決して十分でない。ハニーポットをすり抜けたボットは別の壁にぶつかる必要がある。独立して機能する複数のレイヤーを構築した。
レイヤー1 — ポリモーフィック・ハニーポット。 自動送信の大半を捕捉。フィールド名、隠し方、DOM位置が毎回変わる。
レイヤー2 — 行動分析。 軽量なJavaScriptが基本的なインタラクションシグナルを計測する。ページ読み込みから送信までの時間、マウス移動の有無、キーストロークパターン。2秒未満でインタラクションイベントゼロの送信は人間ではない。このレイヤーは、ハニーポットフィールドをスキップできるほど洗練されたヘッドレスブラウザボットを捕捉する。
レイヤー3 — サーバーサイドのトークン検証。 フォーム描画時にHMAC署名付きタイムスタンプトークンを生成。送信時に検証する。フロントエンドを完全にバイパスする直接POSTリクエスト攻撃を防ぐ。WordPress の nonce ではなくステートレスHMACトークンを選んだのは、nonce はキャッシュされたページで壊れるからだ。私たちのトークンはあらゆるキャッシュ構成——フルページキャッシュ、CDN、静的HTMLキャッシュ——で動作する。
レイヤー4 — サイレントリジェクション。 ボットを捕まえたとき、エラーは返さない。偽の成功レスポンスを返す。ボットは送信が成功したと思い、次に進む。もし403やバリデーションエラーを返せば、ボットオペレーターはスクリプトが検出されたことを知り、調整できる。サイレントリジェクションは、ボットの進化を駆動するフィードバックループを断ち切る。
意図的に入れなかったもの
設計とは、何を入れるかと同じくらい何を外すかの問題だ。意識的に外したものがある:
- クラウドベースのチェック。 送信データを外部サーバーに送ることは一切ない。これはGDPR準拠のため、そしてスパムフィルタがプライバシー問題を生むべきではないという原則のために譲れない。
- コンテンツ分析。 メッセージの文面は検査しない。LLM生成スパムの時代、コンテンツ分析は信頼性が低く処理オーバーヘッドも増える。言語的シグナルではなく行動的シグナルに焦点を当てる。
- ユーザー向け設定パネル。 30個のトグルスイッチが並ぶ管理画面はない。プラグインは合理的なデフォルトで箱から出してすぐ動く。調整が必要なら、開発者フレンドリーなフィルターとフックがある。ただしデフォルトの状態は「ただ動く」。
- reCAPTCHA統合。 他者のソリューションをラップするつもりはない。異なるアプローチのクリーンルーム実装だ。
技術的選択
開発者が気にするかもしれない実装の詳細をいくつか:
サーバーサイドロジックはPHPのみ。 カスタムデータベーステーブルなし。cronジョブなし。バックグラウンドプロセスなし。Contact Form 7 の既存の送信パイプラインに wpcf7_before_send_mail と wpcf7_spam フィルターでフックする。送信を検証し、合否を判定し、あとは邪魔しない。フットプリントは最小限。
クライアントサイドはバニラJavaScript。 jQueryへの依存なし(CF7自体はjQueryを読み込むが)。フレームワークなし。行動分析スクリプトは数キロバイト、非同期で読み込まれ、レンダリングをブロックしない。
アクセシビリティファーストのハニーポット設計。 隠しフィールドは aria-hidden="true" と tabindex="-1" を使い、スクリーンリーダーが完全にスキップする。NVDA、JAWS、VoiceOverでテストし、ハニーポットが支援技術のユーザーに混乱を与えないことを確認した。アクセシビリティは後付けではない。初日から設計の制約として組み込んだ。
道中で学んだこと
CF7 Ultimate Honeypot の開発は、プラグインそのものを超えたいくつかの教訓を与えてくれた。
シンプルが賢さに勝つ
最初のプロトタイプはオーバーエンジニアリングだった。機械学習スコアリング、canvasフィンガープリンティング、ボットシグネチャを追跡するローカルSQLiteデータベース。テストでは見事に動いた。保守とデバッグは悪夢だった。
削ぎ落とした。最終バージョンはストレートな技術——ハニーポット、タイミング分析、暗号トークン——を、部分の総和を超える形で組み合わせている。全てのコンポーネントが理解しやすく、テストしやすく、何かが壊れたときにデバッグしやすい。
最高のセキュリティツールは、中身が退屈なものだ。退屈なコードは信頼できるコードだ。
スパムエコシステムはビジネスである
ボットはランダムな混乱ではない。製品だ。誰かが作り、売り、カスタマーサポートまで提供している。これを理解すると防御の考え方が変わる。
ボットオペレーターは成功した送信あたりのコストを最適化している。あなたのフォームが隣のサイトよりほんの少しスパムしにくければ、多くのボットはあなたを飛ばしてもっと簡単なターゲットに移る。破られないようにする必要はない。攻撃のコストが他の選択肢より高い状態にすればいい。
追加する防御の各レイヤーがコストを引き上げる。ポリモーフィックフィールドにより、ボットオペレーターは静的スクリプトを書けない。行動分析により、人間らしいインタラクションをシミュレートする必要がある。トークン検証により、フロントエンドをスキップできない。サイレントリジェクションにより、失敗をデバッグできない。
これらのレイヤーは単独では突破不可能ではない。しかし組み合わせることで、あなたのフォームをボットオペレーターにとって割に合わない投資にする。
プライバシーとセキュリティは同じ会話だ
繰り返しぶつかったパターンがある。最も人気のあるスパム対策ツールが、ユーザーデータをサードパーティサーバーに送信することを要求するのだ。Akismet はメッセージ内容を送る。reCAPTCHA は行動テレメトリーを送る。他のサービスはIPアドレスとデバイスフィンガープリントを送る。
EUのクライアントと仕事をする開発者にとって、これは矛盾を生む。強力なスパム対策が欲しい、しかし外部APIコールのたびにGDPRの潜在的リスクが生まれる。私たちは早い段階で、CF7 Ultimate Honeypot は全てをローカルで処理すると決めた。データはサーバーから出ない。スパムフィルタのための同意バナーは不要。サードパーティベンダーとのDPA(データ処理契約)署名も不要。
これは単なるコンプライアンスの判断ではない。パフォーマンスの判断だ。外部APIコールなしはレイテンシ追加なし。信頼性の判断だ。外部サービスなしは他社のインフラ障害時のダウンなし。そして信頼の判断だ。サイトオーナーは訪問者に、フォームデータはサーバー上に留まると正直に伝えられる。
今の私たち
CF7 Ultimate Honeypot はWordPress公式プラグインディレクトリで公開されている。1日数件の問い合わせがあるサイトから、数千件のサイトまで稼働中だ。ボットの進化に伴ってもスパム捕捉率は安定している。なぜなら、ポリモーフィックアプローチは今日のボットがどんな姿かを知ることに依存しない——ボットが人間と違う振る舞いをするという、はるかに耐久性のある前提に依存しているからだ。
私たちは今もクライアントサイトを保守している。今もスパムと向き合っている。しかし、600件のスパムについての月曜朝の電話は来なくなった。お問い合わせフォームは機能している。リードは届いている。ボットは丁寧な「メッセージありがとうございます」を受け取り、それだけだ。
4つのプラグインをガムテープで繋ぎ合わせてコンタクトフォームを守ることに疲れた開発者、あるいはクリーンな受信箱を諦めたサイトオーナー。私たちはあなたのためにこれを作った。世界にもう一つWordPressプラグインが必要だったからではなく、存在しないツールが必要で、きっと他の人も同じだろうと思ったからだ。
まとめ
-
既存ソリューションにはリアルなトレードオフがある。 Akismet はデータを外部に送る。reCAPTCHA はパフォーマンスとコンバージョンを犠牲にする。基本的なハニーポットは予測されやすい。それを認めて別のものを作ることは恥ではない。
-
ポリモーフィック防御は静的防御に勝る。 ページ読み込みのたびに同じ見た目の保護は、いずれリバースエンジニアリングされる。リクエストごとにフォームの内部構造を変えることで、ボットに毎回新しい問題を解かせる。
-
防御を多層にせよ。 単一の技術で全てのボットを止めることはできない。ハニーポット、行動分析、暗号トークン、サイレントリジェクションが連携して、フォームを攻撃コストの高いものにする。
-
プライバシーは機能のチェックボックスではなく設計の制約。 全てをローカルで処理することは、よりシンプルで、より速く、より信頼性が高く、余計な書類仕事なしでGDPRの正しい側にいられる。
-
退屈なコードを出荷せよ。 最高のセキュリティツールは、理解しやすく、テストしやすく、深夜2時に修正しやすいものだ。賢さはアーキテクチャに使え。実装はストレートに保て。
私たちは不満から CF7 Ultimate Honeypot を作った。この開発ストーリーを共有するのは、その不満は普遍的なものだと思うから——そして解決策は複雑である必要がないからだ。
CF7 Ultimate Honeypot は WordPress公式プラグインディレクトリ で無料公開中。多層防御アプローチの技術的詳細を知りたい方は、見えないセキュリティ:ユーザーを煩わせないフォーム保護 をお読みください。