お問合せフォームに「私はロボットではありません」を導入する方法|簡単にできるスパム対策

お問合せフォームの簡単スパム対策 DX化
お問合せフォームの簡単スパム対策

お問い合わせフォームからのスパム送信に悩まされていませんか?多くの中小企業では、ウェブサイトのフォーム経由で大量の不要なメッセージやボットによる自動送信に対処する必要があります。これらは業務効率を下げるだけでなく、セキュリティリスクにもなり得ます。

Googleが提供するGoogle reCAPTCHA v2でこの問題を簡単に解決できます。特にReactで構築したフォームに簡単に導入でき、技術的な専門知識が限られた中小企業でも実装可能です。このガイドでは、ReactフォームにGoogle reCAPTCHA v2を導入する具体的な手順を解説します。

今回は、スパム対策の重要性、実装手順、gasでのスパム検証方法まで段階的に説明します。この記事を参考に、お問合せフォームをより安全で効率的なものに改善しましょう。

お問合せフォームにGoogle reCAPTCHA v2を導入する理由とメリット

お問合せフォームに適切なスパム対策を施すことは、現代のウェブ開発において必須の課題となっています。なぜGoogle reCAPTCHA v2が効果的な選択肢なのか、簡単に説明します。

スパム防止にreCAPTCHAが有効な理由

フォームスパムは単なる迷惑以上の問題です。企業にとって、大量のスパムメールは以下のような深刻な影響をもたらします:

  • 本当の顧客からの問い合わせの見落とし
  • スタッフの時間とリソースの無駄遣い
  • サーバーリソースの過剰消費
  • 悪意のあるデータ送信によるセキュリティリスク

Google reCAPTCHAは「Completely Automated Public Turing test to tell Computers and Humans Apart(コンピュータと人間を区別するための完全自動化された公開チューリングテスト)」の略で、その名の通り人間とボットを区別します。画像認識チャレンジやチェックボックスなどのインタラクションを通じて、自動送信ボットをブロックしながら、実際のユーザーには最小限の負担で認証を完了させることができます。

Google reCAPTCHA v2とv3の違い

Google reCAPTCHAには主にv2とv3の2つのバージョンがあり、それぞれ特徴が異なります:

reCAPTCHA v2:

  • ユーザーによる明示的なアクション(チェックボックスのクリックや画像選択)が必要
  • 視覚的に確認できる要素がフォームに表示される
  • 明確な「私はロボットではありません」の確認が必要
  • 実装が比較的シンプル

reCAPTCHA v3:

  • バックグラウンドで動作し、ユーザーのアクションは不要
  • ユーザーインターフェイスに表示される要素はない
  • ユーザー行動の分析に基づくスコアリングシステム
  • より複雑な実装と調整が必要

reCAPTCHA v2を選ぶべき理由

今回は、reCAPTCHA v2を実装導入する手順をご紹介します。わたしが、reCAPTCHA v2を選んだ理由は主に以下です。

  1. 実装の容易さ: 技術スタッフが限られている中小企業でも、比較的簡単に導入できます。
  2. 透明性: ユーザーに明示的にスパム対策を行っていることを示すことで、セキュリティへの配慮をアピールできます。
  3. カスタマイズの柔軟性: サイトのデザインに合わせて表示位置やスタイルを調整できます。
  4. 効果の確実性: ボットの多くを効果的にブロックしながら、誤判定によるユーザー体験の低下を最小限に抑えられます。

reCAPTCHA v3は高度なセキュリティを提供しますが、スコアの閾値設定や誤判定時の対応など、より専門的な知識と継続的な監視が必要です。最小限のリソースで導入&運用を目指すなら、v2の方が費用対効果に優れた選択と考えました。

GASのエンドポイントが誰にでも公開されていることが危険な理由

前回の記事で、Google Apps Script(GAS)をフォーム送信先として利用する手順をご紹介しました。GASをフォームの送信先としてするためにWEBアプリとしてデプロイする必要があります。記事内でも軽く触れましたがこの方法はセキュリティリスクが潜んでいます:

  • GASのウェブアプリURLは誰でもアクセス可能
  • スパマーがフォームをバイパスして直接エンドポイントにリクエストを送信可能
  • フロントエンドのバリデーションだけでは不十分
  • 大量の自動送信によるサービスの過負荷リスク

reCAPTCHAを実装することで、正規のユーザーからの送信のみをバックエンドで処理するよう制限でき、このリスクを大幅に軽減できます。特にGASエンドポイントを使用している場合、reCAPTCHAによる検証は不可欠なセキュリティ層となります。

お問合せフォームにGoogle reCAPTCHA v2を組み込む手順と実装方法

reCAPTCHA v2をReactフォームに導入する具体的な手順を見ていきましょう。技術的な知識が限られている方でも、以下のステップに従うことで安全に実装できます。

Google reCAPTCHAのサイトキー・シークレットキーを取得する

まず、reCAPTCHAを使用するために必要なキーを取得します:

  1. Google reCAPTCHA管理コンソールにアクセスします(Googleアカウントでログインが必要)
  2. 「新しいサイトを登録」ボタンをクリックします
  3. 以下の情報を入力します:
    • ラベル:サイト識別用の名前(例:「会社名お問い合わせフォーム」)
    • reCAPTCHAタイプ:「reCAPTCHA v2」を選択
    • 「「私はロボットではありません」チェックボックス」を選択
    • ドメイン:reCAPTCHAを使用するサイトのドメイン(例:localhost , example.com)
    • メールアドレス:通知を受け取るアドレス
  4. 利用規約に同意し、「登録」ボタンをクリックします
  5. 登録完了画面で表示される「サイトキー」と「シークレットキー」を安全に保存します
    • サイトキー:フロントエンド(React)で使用
    • シークレットキー:バックエンド(API、GASなど)で使用。絶対に公開しないでください

サイトキーとシークレットキーは、それぞれフロントエンドとバックエンドでreCAPTCHAの検証に使用されるため、安全に管理することが重要です。

react-google-recaptchaライブラリをインストールする

Reactプロジェクトで簡単にreCAPTCHAを実装するために、専用のライブラリをプロジェクトにインストールします:

# npmの場合
npm install react-google-recaptcha --save

# yarnの場合
yarn add react-google-recaptcha

このライブラリは、GoogleのreCAPTCHA v2をReactコンポーネントとして提供し、フォームへの統合を簡素化します。

フォームにreCAPTCHAを設置してトークンを取得する

次に、Reactコンポーネント内でreCAPTCHAを実装します:

import React, { useState, useRef } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';

const ContactForm = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });
  const [captchaToken, setCaptchaToken] = useState(null);
  const recaptchaRef = useRef(null);

  // 入力フィールドの変更を処理する関数
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prevState => ({
      ...prevState,
      [name]: value
    }));
  };

  // reCAPTCHAの変更を処理する関数
  const handleCaptchaChange = (token) => {
    setCaptchaToken(token);
  };

  // フォーム送信時の処理
  const handleSubmit = async (e) => {
    e.preventDefault();
    
    // captchaTokenがnullの場合、検証が完了していないためアラートを表示
    if (!captchaToken) {
      alert('「私はロボットではありません」にチェックを入れてください');
      return;
    }
    
    // このあとの送信処理は後ほど実装
    console.log('フォームデータ:', formData);
    console.log('reCAPTCHAトークン:', captchaToken);
  };

  return (
    <form onSubmit={handleSubmit} className="contact-form">
      <div className="form-group">
        <label htmlFor="name">お名前</label>
        <input
          type="text"
          id="name"
          name="name"
          value={formData.name}
          onChange={handleChange}
          required
        />
      </div>
      
      <div className="form-group">
        <label htmlFor="email">メールアドレス</label>
        <input
          type="email"
          id="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
          required
        />
      </div>
      
      <div className="form-group">
        <label htmlFor="message">お問い合わせ内容</label>
        <textarea
          id="message"
          name="message"
          value={formData.message}
          onChange={handleChange}
          required
          rows="5"
        ></textarea>
      </div>
      
      <div className="recaptcha-container">
        <ReCAPTCHA
          ref={recaptchaRef}
          sitekey="あなたのサイトキーをここに入力"
          onChange={handleCaptchaChange}
          hl="ja" // 日本語表示にする場合
        />
      </div>
      
      <button type="submit" className="submit-button">送信する</button>
    </form>
  );
};

export default ContactForm;

このコードでは、reCAPTCHAコンポーネントを追加し、ユーザーが「私はロボットではありません」をクリックするとトークンが生成され、captchaTokenステートに保存されます。フォーム送信時にこのトークンがない場合は送信をブロックします。

送信時にreCAPTCHAトークンをAPIに渡す方法

フォーム送信時に、reCAPTCHAトークンをバックエンドAPIに送信して検証する方法を実装します:

// 先ほどのhandleSubmit関数を拡張
const handleSubmit = async (e) => {
  e.preventDefault();
  
  // captchaTokenがnullの場合、検証が完了していないためアラートを表示
  if (!captchaToken) {
    alert('「私はロボットではありません」にチェックを入れてください');
    return;
  }
  
  // 送信中状態を管理する場合
  setIsSubmitting(true);
  
  try {
    // FormDataオブジェクトの作成
    const dataToSend = {
      ...formData,
      recaptchaToken: captchaToken // reCAPTCHAトークンを含める
    };
    
    // APIエンドポイントへのPOSTリクエスト
    const response = await fetch('あなたのAPIエンドポイントURL', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(dataToSend)
    });
    
    const result = await response.json();
    
    if (response.ok) {
      // 送信成功時の処理
      alert('お問い合わせを送信しました。ありがとうございます。');
      // フォームのリセット
      setFormData({ name: '', email: '', message: '' });
      // reCAPTCHAのリセット
      recaptchaRef.current.reset();
      setCaptchaToken(null);
    } else {
      // エラーメッセージの表示
      alert(`エラーが発生しました: ${result.error || '不明なエラー'}`);
    }
  } catch (error) {
    console.error('送信エラー:', error);
    alert('送信中にエラーが発生しました。後ほど再度お試しください。');
  } finally {
    setIsSubmitting(false);
  }
};

このコードでは、フォームデータと一緒にreCAPTCHAトークンをバックエンドに送信します。バックエンドでこのトークンの有効性を検証することで、人間からの正当なリクエストであることを確認できます。

ReactフォームにGoogle reCAPTCHA v2を導入して安全性を高めよう

ここまでの実装により、ReactフォームにGoogle reCAPTCHA v2が正常に統合されました。この導入によって得られるメリットは大きく、スパムボットからの自動送信を効果的に防止しながら、実際のユーザーには最小限の負担でフォームを利用してもらうことができます。

追加のカスタマイズとして、以下のようなオプションも検討できます:

  • reCAPTCHAのテーマ設定(theme="dark"で暗色テーマに変更可能)
  • サイズの調整(size="compact"でコンパクト表示)
  • タブインデックスの設定(tabindex="0"でタブキーでのフォーカス順序を制御)

また、reCAPTCHAは定期的にトークンが失効するため、長時間フォームを開いたままにしていると有効期限が切れることがあります。この場合、送信前に再度検証が必要になるため、エラーメッセージで適切にユーザーに案内することも重要です。

Google Apps Scriptやバックエンド側でのreCAPTCHA検証方法(任意)

フロントエンドでreCAPTCHAを実装するだけでは完全なセキュリティは確保できません。バックエンド側(受け取り側)での検証が不可欠です。ここでは概要を紹介します。

トークン検証に使うGoogleのAPIエンドポイント

Google reCAPTCHAトークンの検証には、Googleが提供する専用APIエンドポイントを使用します:

https://www.google.com/recaptcha/api/siteverify

このエンドポイントにPOSTリクエストを送信し、以下のパラメータを含める必要があります:

  • secret: あなたのreCAPTCHAシークレットキー
  • response: フロントエンドから受け取ったreCAPTCHAトークン
  • remoteip: (オプション)ユーザーのIPアドレス

レスポンスは以下のようなJSON形式で返されます:

{
  "success": true|false,
  "challenge_ts": "タイムスタンプ",
  "hostname": "ユーザーがreCAPTCHAを解いたサイトのホスト名",
  "error-codes": ["エラーコードの配列(エラー時のみ)"]
}

successフィールドがtrueの場合のみ、トークンが有効であると判断できます。

GAS側での検証例(MailApp送信前のチェック)

Google Apps Script(GAS)でreCAPTCHAを検証する基本的な例を示します:

function doPost(e) {
  // POSTリクエストからデータを取得
  const data = JSON.parse(e.postData.contents);
  const { name, email, message, recaptchaToken } = data;
  
  // reCAPTCHAトークンの検証
  const recaptchaVerification = verifyRecaptcha(recaptchaToken);
  
  // 検証に失敗した場合はエラーレスポンスを返す
  if (!recaptchaVerification.success) {
    return ContentService.createTextOutput(JSON.stringify({
      success: false,
      error: 'reCAPTCHA検証に失敗しました'
    })).setMimeType(ContentService.MimeType.JSON);
  }
  
  try {
    // 検証成功後にメール送信など処理を実行
    MailApp.sendEmail({
      to: "your-email@example.com",
      subject: "ウェブサイトからのお問い合わせ",
      body: `名前: ${name}\nメールアドレス: ${email}\n\nメッセージ:\n${message}`
    });
    
    // 成功レスポンスを返す
    return ContentService.createTextOutput(JSON.stringify({
      success: true,
      message: 'お問い合わせを送信しました'
    })).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    // エラーレスポンスを返す
    return ContentService.createTextOutput(JSON.stringify({
      success: false,
      error: error.toString()
    })).setMimeType(ContentService.MimeType.JSON);
  }
}

// reCAPTCHAトークンを検証する関数
function verifyRecaptcha(token) {
  const SECRET_KEY = "あなたのシークレットキーをここに入力";
  
  // Google reCAPTCHA検証APIにリクエスト
  const response = UrlFetchApp.fetch("https://www.google.com/recaptcha/api/siteverify", {
    method: "post",
    payload: {
      secret: SECRET_KEY,
      response: token
    }
  });
  
  // レスポンスをJSONとしてパース
  return JSON.parse(response.getContentText());
}

このスクリプトでは、フロントエンドから受け取ったreCAPTCHAトークンをGoogle APIで検証し、有効な場合のみメール送信などの処理を実行します。

成功/失敗時の応答の扱い方と注意点

バックエンド側での検証結果に応じた適切な対応が重要です:

成功時:

  • ユーザーに成功メッセージを表示
  • フォームをリセット
  • 必要に応じて別ページへリダイレクト

失敗時:

  • 具体的なエラー内容を開示しすぎない(セキュリティリスク)
  • ユーザーフレンドリーなエラーメッセージを表示
  • reCAPTCHAの再検証を促す
  • 繰り返し失敗する場合は代替の連絡手段を案内

注意点:

  • シークレットキーは絶対に公開しない
  • トークンの有効期限は約2分間(長時間フォームを開いていると期限切れになる)
  • 特に以下の項目は注意が必要:
    • スコアが低すぎる(0.1未満)場合は明らかなボット
    • hostnameが予期しないドメインの場合はトークンが盗用されている可能性
    • 同一IPからの短時間での多数リクエストはブロックを検討

適切なエラーハンドリングと安全なトークン検証により、スパム対策の効果を最大化しながらユーザー体験を損なわないバランスを実現できます。

以下にまとめの部分のみを出力します:

Google reCAPTCHAのコストに関する部分を追加します。以下がその部分のみの出力です:

Google reCAPTCHAの導入コストとランニングコスト

reCAPTCHA v2を導入する際のコスト面も重要な検討ポイントです。中小企業にとって嬉しいことに、通常の使用であれば以下のようなコスト構造となっています:

初期導入コスト:

  • Google reCAPTCHA v2の基本サービス: 無料
  • APIキー取得: 無料
  • 実装に必要な開発コスト: 内製の場合は工数のみ、外注の場合は5〜10万円程度

ランニングコスト:

  • 通常利用(月間100万回の検証まで): 無料
  • 大量利用(月間100万回を超える検証): 1,000,000リクエスト以降、$1.00/1,000リクエスト
  • 関連するサーバーやインフラコスト: 既存システムを使用するため実質的に追加コストなし

小規模事業では、月間の問い合わせ数が数百から数千程度であることを考えると、無料枠で十分対応可能です

注意点として、reCAPTCHA Enterprise(企業向け高度バージョン)を利用する場合は別料金体系となりますが、通常の中小企業であれば標準バージョンで十分な機能とセキュリティレベルを確保できます。

まとめ

本記事では、React実装したお問合せフォームにGoogle reCAPTCHA v2を導入する方法について解説しました。ウェブサイトを公開する上で、スパム対策は業務効率化とセキュリティ強化の両面で重要な課題です。reCAPTCHA v2は、実装の容易さと効果的なスパム防止機能を兼ね備えた理想的なツールです。

サイトキーとシークレットキーの取得から始まり、react-google-recaptchaライブラリを使ったフロントエンド実装、そしてバックエンドでの検証まで、一連の流れを理解することで、技術的な専門知識が限られていても導入することが可能です。

特に重要なのは、フロントエンドだけでなくバックエンド側での検証も必ず実装することです。これにより、フォームをバイパスした不正なAPIリクエストからもシステムを守ることができます。

reCAPTCHA v2の導入により、本当に必要なお問い合わせだけを受け取り、業務効率を向上させながら、ウェブサイトのセキュリティを強化しましょう。小さな対策が、大きな安心につながります