この記事では、Vue.jsを使ったお問い合わせフォームにGoogle reCAPTCHAを導入し、スパム対策を強化する方法を解説します。このフォームは、入力画面、確認画面、完了画面の3ステップで構成され、メール送信機能も実装されています。また、Google reCAPTCHAのsiteverifyエンドポイント
についても詳しく解説します。
目次
- プロジェクト概要
- Google reCAPTCHA の設定
- ソースコード解説
- フロントエンド (Vue.js)
- バックエンド (PHP)
- siteverifyエンドポイントについて
- メリットと注意点
- 動作確認
1. プロジェクト概要
このプロジェクトでは、以下の技術を使用しています:
- Vue.js: 3.x (CDN経由で最新バージョンを使用)
- Vue Router: 4.x (CDN経由で最新バージョンを使用)
- Tailwind CSS: 3.x (CDN経由で最新バージョンを使用)
- Google reCAPTCHA v3
- PHP: 8.2.22
- サーバー環境: エックスサーバー
2. Google reCAPTCHA の設定
- Google reCAPTCHA サイトに登録
- Google reCAPTCHA管理画面 にアクセスし、サイトを登録。
- reCAPTCHA v3 を選択し、サイトキーとシークレットキーを取得します。
- JavaScriptでreCAPTCHAを読み込む
フォームに以下のスクリプトを追加します:
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
- サーバー側でreCAPTCHAを検証
トークンの有効性をサーバー側で検証する必要があります。siteverifyエンドポイント
を利用します(後述)。
3. ソースコード解説
(1) フロントエンド (Vue.js)
以下は、reCAPTCHAトークンの取得を含むVue.jsフォームのコードです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue.js お問い合わせフォーム</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-router@4"></script>
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center">
<div id="app" class="bg-white shadow-md rounded p-6 w-full max-w-md mx-auto">
<router-view></router-view>
</div>
<script>
const { createRouter, createWebHistory } = VueRouter;
const { createApp } = Vue;
// 入力画面
const InputForm = {
template: `
<div>
<h1 class="text-2xl font-bold mb-4">お問い合わせフォーム</h1>
<form @submit.prevent="goToConfirm">
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700">名前:</label>
<input v-model="formData.name" type="text" id="name" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div class="mb-4">
<label for="email" class="block text-sm font-medium text-gray-700">メールアドレス:</label>
<input v-model="formData.email" type="email" id="email" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div class="mb-4">
<label for="message" class="block text-sm font-medium text-gray-700">お問い合わせ内容:</label>
<textarea v-model="formData.message" id="message" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"></textarea>
</div>
<button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">確認画面へ</button>
</form>
</div>
`,
data() {
return {
formData: JSON.parse(localStorage.getItem('formData')) || { name: '', email: '', message: '' },
};
},
methods: {
goToConfirm() {
localStorage.setItem('formData', JSON.stringify(this.formData));
this.$router.push({ name: 'confirm' });
},
},
};
// 確認画面
const ConfirmForm = {
template: `
<div>
<h1 class="text-2xl font-bold mb-4">内容の確認</h1>
<p class="mb-2"><strong>名前:</strong> {{ formData.name }}</p>
<p class="mb-2"><strong>メールアドレス:</strong> {{ formData.email }}</p>
<p class="mb-4"><strong>お問い合わせ内容:</strong> {{ formData.message }}</p>
<div class="flex space-x-4">
<button @click="goBack" class="w-1/2 bg-gray-600 text-white py-2 px-4 rounded hover:bg-gray-700">戻る</button>
<button @click="sendForm" class="w-1/2 bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">送信する</button>
</div>
</div>
`,
data() {
return {
formData: JSON.parse(localStorage.getItem('formData')) || { name: '', email: '', message: '' },
};
},
methods: {
goBack() {
this.$router.push({ name: 'input' });
},
sendForm() {
grecaptcha.ready(() => {
grecaptcha.execute('YOUR_SITE_KEY', { action: 'submit' }).then((token) => {
fetch('backend/contact.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...this.formData, recaptcha_token: token }),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
localStorage.removeItem('formData');
this.$router.push({ name: 'complete' });
} else {
alert('送信に失敗しました: ' + data.error);
}
})
.catch(() => alert('エラーが発生しました。通信環境を確認してください。'));
});
});
},
},
};
// 完了画面
const CompleteForm = {
template: `
<div>
<h1 class="text-2xl font-bold mb-4">送信が完了しました</h1>
<p>お問い合わせいただきありがとうございます。</p>
<button @click="goToInput" class="mt-4 w-full bg-indigo-600 text-white py-2 px-4 rounded hover:bg-indigo-700">フォームに戻る</button>
</div>
`,
methods: {
goToInput() {
this.$router.push({ name: 'input' });
},
},
};
const routes = [
{ path: '/', name: 'input', component: InputForm },
{ path: '/confirm', name: 'confirm', component: ConfirmForm },
{ path: '/complete', name: 'complete', component: CompleteForm },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
const App = {
template: '<router-view></router-view>',
};
const app = createApp(App);
app.use(router);
app.mount('#app');
</script>
</body>
</html>
(2) バックエンド (PHP)
以下は、reCAPTCHAトークンを検証し、メールを送信するPHPコードです。
<?php
header('Content-Type: application/json');
// リクエストデータの取得
$data = json_decode(file_get_contents('php://input'), true);
// reCAPTCHA の検証
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'YOUR_SECRET_KEY'; // シークレットキーを設定
$recaptcha_response = $data['recaptcha_token'];
$response = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($response, true);
if (!$recaptcha['success']) {
echo json_encode(['success' => false, 'error' => 'reCAPTCHA 検証に失敗しました。']);
exit;
}
// 管理者への通知メール
$toAdmin = 'admin@example.com';
$subjectAdmin = 'お問い合わせフォームからのメッセージ';
$messageAdmin = "名前: {$data['name']}\nメールアドレス: {$data['email']}\nメッセージ: {$data['message']}";
$headersAdmin = 'From: noreply@example.com';
$mailToAdmin = mail($toAdmin, $subjectAdmin, $messageAdmin, $headersAdmin);
// 問い合わせ者への自動返信メール
$toUser = $data['email'];
$subjectUser = 'お問い合わせありがとうございます';
$messageUser = "{$data['name']} 様\n\nお問い合わせありがとうございます。\n以下の内容で受け付けました。\n----------------------\nお名前: {$data['name']}\nメールアドレス: {$data['email']}\nお問い合わせ内容:\n{$data['message']}\n----------------------\n\n担当者より折り返しご連絡いたします。";
$headersUser = 'From: noreply@example.com';
$mailToUser = mail($toUser, $subjectUser, $messageUser, $headersUser);
if ($mailToAdmin && $mailToUser) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'メール送信に失敗しました。']);
}
4. siteverifyエンドポイントについて
Google reCAPTCHA の siteverify エンドポイント は、reCAPTCHA トークンをサーバー側で検証するためのAPIです。これを利用することで、reCAPTCHAによって生成されたトークンが有効かどうかを確認します。
siteverifyの役割
- クライアントサイドで生成されたreCAPTCHAトークンを検証。
- サーバー側でGoogleの検証結果を受け取り、信頼性を判断。
リクエスト構成
- エンドポイント URL:
https://www.google.com/recaptcha/api/siteverify
- 必要なパラメータ:
secret
: サーバー側で管理するシークレットキーresponse
: クライアントサイドから送信されたトークン
例: PHPによる検証コード
以下はPHPでsiteverifyエンドポイントを使ってreCAPTCHAトークンを検証するコードです。
$recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
$recaptcha_secret = 'YOUR_SECRET_KEY';
$recaptcha_response = $data['recaptcha_token'];
$response = file_get_contents($recaptcha_url . '?secret=' . $recaptcha_secret . '&response=' . $recaptcha_response);
$recaptcha = json_decode($response, true);
if (!$recaptcha['success']) {
echo json_encode(['success' => false, 'error' => 'reCAPTCHA 検証に失敗しました。']);
exit;
}
レスポンス例
{
"success": true,
"challenge_ts": "2024-11-22T12:34:56Z",
"hostname": "example.com",
"score": 0.9,
"action": "submit"
}
レスポンスのフィールド:
フィールド名 | 説明 |
---|---|
success | 検証が成功したかどうか(true または false ) |
challenge_ts | ユーザーがreCAPTCHAを解決したタイムスタンプ |
hostname | トークンが発行されたドメイン |
score | 信頼スコア (v3のみ)。0.0〜1.0で値が高いほど信頼できる |
action | フロントエンドで指定したアクション名 |
error-codes | 検証が失敗した場合のエラーコード |
5. メリットと注意点
メリット
- スパム対策
reCAPTCHAを導入することで、自動化されたスパム送信を防ぐことができます。 - 非侵入型保護 (v3)
reCAPTCHA v3ではユーザー体験を妨げることなく、スコアによる判断が可能です。 - 簡単な統合
Vue.jsおよびPHPとの統合が容易で、柔軟なフォーム設計が可能です。
注意点
- HTTPS必須
reCAPTCHAはHTTPS環境でのみ動作します。 - シークレットキーの管理
シークレットキーは絶対にサーバー外に漏れないようにしてください。 - スコアの確認
reCAPTCHA v3では、score
を確認し、一定の閾値を設定して適切に動作を制御してください。
6. 動作確認
以下の手順で動作を確認できます。
- フォーム入力
名前
,メールアドレス
,お問い合わせ内容
を入力。- 「確認画面へ」をクリック。
- 確認画面
- 入力内容が正しいかを確認。
- 「送信する」をクリックすると、reCAPTCHAトークンが生成されます。
- メール送信および完了画面
- バックエンドでreCAPTCHAトークンが検証され、メールが送信されます。
- 「送信が完了しました」と表示されます。
スクリーンショット例
入力画面
確認画面
完了画面
この記事では、Vue.jsとreCAPTCHAを利用したお問い合わせフォームの作成方法を解説しました。この手順を活用して、安全性の高いフォームを作成してください!
コメント