こんにちは!開発本部 SJC 共同開発推進室の對島です。
皆さんは、Webサイトで「車を選んでください」とか「信号機はどれ?」といった画像認証が出てきて、「あぁ〜!もう面倒くさいな!」と思ったことはありませんか?
以前までは私もそう思っていました、、、
でも最近、業務でボット対策を任され、裏側で繰り広げられていたボットとの闘いがそこにはありました。今回は、AWS初心者の私が知ったAmazon SESの危機と、それを救うhCaptchaについて、等身大でお届けします!
1. 私たちのシステムと、忍び寄る「ボット」の影
本題に入る前に、私が担当しているシステムの構成を簡単にご紹介します。 ユーザーがメールアドレスを入力すると、本人確認のメールが自動で届くシンプルな仕組みです。
-
フロントエンド: Vue.js(S3で公開)
-
バックエンド: API Gateway + AWS Lambda
-
メール送信: Amazon SES

しかしある日、先輩からこう言われました。
先輩:「本番環境でバウンスメールが急増してる。これ、ボットの嫌がらせだわ。早急にCAPTCHA入れて。」
私:「(…バウンス?ボット?キャプチャ?必殺技の名前かな?)」
2. そもそも「ボット」って何者?
まずは正体不明の敵を知ることからスタートです。
調べてみると、エンジニアリングの世界でいうボットとは「人間に代わって自動で作業を行うプログラム」のこと。
-
良いボット:24時間いつでも問い合わせに応答してくれるチャットボットや、地震速報などの大切な情報をいち早くSNSで発信してくれる自動プログラム
-
悪いボット:サイトの隙を突いて、人間には不可能なスピード(1秒間に何百回)で、デタラメな情報を送りつけてくる攻撃プログラム。
今回の敵はもちろん後者。「人間が1人ずつ入力する」ことを前提に作られた私たちのシステムに対して、「機械のスピード」でゴミデータを投げ込んでくる……そんな、ずるい相手でした。
3. なぜボットの「大量登録」がそんなに怖いの?
「ボットがメールアドレスを勝手に登録するくらい、放っておけばいいじゃん。後で消せばいいじゃん。」…なんて思っていた時期が私にもありました。でも、私たちがメール送信に使っているAmazon SES(Simple Email Service)の世界は、そんなに甘くありませんでした。
SESの「信用(レピュテーション)」
私たちが普段利用しているGmailなどのメールサービスは、届いたメールを「受信トレイ」に入れるか「迷惑メールフォルダ」に振り分けるかを判断する際、送信元の「レピュテーション(信頼度)」を厳格にチェックしています。
この信頼度は、主に以下の指標でスコア化されています。
-
バウンス率(宛先不明のエラー率)
-
苦情率(ユーザーからの迷惑メール報告率)
もし、ボットによってアプリケーションに「存在しないメールアドレス」が大量に登録され、それらに対して自動でメールが送信されると、バウンス率が急上昇します。
バウンス率が悪化して信頼度が落ちると、最終的にはAmazon SESの利用停止だけでなく、Gmailなどの受信側サーバーから完全にブロック(着信拒否)される事態を招いてしまいます。
| 項目 | 内容 | 監視基準 |
| バウンス | 存在しないアドレスに送ってエラーで戻る割合 | 5%超でアラート、10%超で停止リスク |
| 苦情率 | 受信者が「迷惑メール!」と報告した割合 | 0.1%超でアラート、0.5%超で停止リスク |
最悪のシナリオ:巻き添え停止
さらに恐ろしいのは、AWS SESの信用度は「AWSアカウント単位」で管理されているという点です。つまり、たった一つのシステムがボットに悪用されて信用が落ちると、同じアカウントを使っている社内の全サービスまで、「迷惑メール」扱いされて届かなくなってしまうのです。
他システムの大事な通知メールも届かなくなる…そんな事態になったら、私のエンジニア人生は2年目で終わりを迎えます。
4.「ボット対策」って具体的に何すればいい?
ボット対策が必要だと分かったものの、AWS初心者の私は「何をどうすれば防げるのか」がさっぱり分かりませんでした。
「怪しいIPアドレスを1つずつブロックする…?」 「海外からのアクセスを全部禁止にする…?」
色々調べてみると、対策にはいくつか種類がありそう。
-
IP制限:特定の場所からのアクセスを遮断する(でもボットはIPを変えてくるはず…)
-
レートリミット:短時間の連打を制限する(でもボットはゆっくり攻撃してくるはず…)
-
CAPTCHA(キャプチャ):「人間かボットか」をテストで判別する(これが一番確実そう!)
ここでようやく、あの「信号機の画像を選んでください」という画面の正体がCAPTCHAだと知りました。(あれちゃんと名前あったんですね…)

5. なぜreCAPTCHAじゃなくて「hCaptcha」?
CAPTCHAといえば、あの「私はロボットではありません」というGoogleのやつ(reCAPTCHA)しか目にしたことがなかったので、それ一択だと思っていました。

でも、調べてみると実は奥が深い!
| サービス | 特徴 | メリット / デメリット |
| ImageCaptcha (Python) | 自前で文字画像を作る | 無料だけど、今のAIボットには秒で突破されてしまうらしい。 |
| reCAPTCHA (Google) | 超有名・王道 | 有名なので安心感あるが、無料枠に制限あり。認証時にユーザーの挙動データを収集するため、プライバシー保護の観点で気になることも…。 |
| hCaptcha (今回採用!) | プライバシー重視 | ユーザーの個人データを広告目的に利用しない。無料枠がデカい(月100万件!)。 |
最近、Google reCAPTCHAの実質有料化(無料枠の縮小)が話題なこともあり、「プライバシーをしっかり守りつつ、コスパも最強」な hCaptcha を選ぶことにしました!
どうやって始めるの?
導入自体はとても簡単でした!まずはhCaptcha公式サイトでアカウントを作成し、管理画面から以下の2つを取得するだけです。
-
Site Key: フロントエンド(Vue.js)に貼り付ける公開用の鍵
-
Secret Key: バックエンド(Lambda)で検証に使う秘密の鍵
詳しい手順は公式サイトに分かりやすく載っているので、興味がある方はぜひ覗いてみてください。
6. いざ実装!AWS初心者がぶつかった壁
ボット対策導入後のざっくりとした構成図がこちらです。(赤枠が新たに追加したボット対策部分)

①全体の流れ
ユーザーが画面を開いてから、メールが届くまでの「4ステップ」を整理しました。
-
画面を表示する(S3) ブラウザでサイトにアクセスすると、S3に置かれたVue.jsが読み込まれ、画面が表示されます。
-
「人間か?」のチェック(判定用Lambda:VPC外) ユーザーがパズルを解くと、その「証拠(トークン)」をAPI Gateway経由でVPC外のLambdaに送ります。
- API Gatewayとは?: 簡単に言うと、ブラウザからのリクエストを各プログラム(Lambdaなど)へ振り分ける「窓口」のようなサービスです。
-
仕事: hCaptchaのサーバーへ「この証拠、本物?」と聞きに行きます。
-
ポイント: インターネットに直接つながる場所にいるので、追加コストなしで外の世界と通信が可能。
-
「送信」ボタンをクリック(メインLambda:VPC内) 認証OKの返事を受けて、ようやく「送信」ボタンが有効になります。今度は安全なネットワーク(VPC)内にいる通常のLambdaを呼び出します。
-
メールが届く(SES) Amazon SESが、ユーザーのメールアドレスへ実際にメールを届けます。
②「お金」と「ネットワーク」の罠
私たちのシステムでは、セキュリティを高めるためにプログラム(Lambda)を「VPC」という隔離されたネットワークの中に配置することがあります。しかし、ここから外のhCaptchaへ通信しようとすると、専用の出口(NAT Gateway)を設置する必要があり、なかなかのコストがかかってしまいます。
そこで今回は、「ボット検証用のLambdaだけをVPCの外に置く」という工夫をしました。これにより、セキュリティを保ちつつ、無駄な費用を抑えることができました!インフラの構成は予算や目的に合わせて一番良い形を泥臭く考える。そんなエンジニアらしい工夫の楽しさを学んだ瞬間でした。
③ フロントエンド(Vue.js)の実装
まずはユーザーが目にする画面です。Vue.jsのテンプレートに、hCaptchaを表示するための「箱」を用意します。
|
1 2 3 |
<div class="container"> <div class="h-captcha" :data-sitekey="hcaptchaSiteKey" ref="hcaptchaWidget"></div> </div> |
ユーザーが認証をクリアした時にもらえる「合言葉(トークン)」を使って、APIを順番に呼び出します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 1. まず「合言葉」をLambdaへ送り、人間か判定してもらう this.$store.dispatch('requestApi/verifyHcaptchaAction', { url: hcaptchaApiUrl, params: { hcaptcha_response: this.hcaptchaToken } }) .then(hcaptchaResult => { if (!hcaptchaResult.success) { throw new Error("認証失敗!"); // ボットならここでストップ! } // 2. 認証OKなら、ようやく「会員登録」を実行! return this.$store.dispatch('requestApi/postApiInfoAction', { ... }); }) .then(() => { this.$router.push("/success"); // 成功! }) |
具体的な実装方法や仕組みなどは、公式サイトの開発者ガイドに載っていますので、興味のある方はぜひ!
7. まとめ:「面倒な認証」の裏側を知る
実装完了後、画面に表示されたのは、見慣れた「私は人間です」の画面。

以前の私は「あぁ、またこれか…」と思っていましたが、今の私は違います。
なんだか誇らしい気持ちになります。
普段何気なく使っている「面倒なひと手間」は、裏側でエンジニアが必死にシステムを守るために努力した証だったんだと。
2年目の若造ですが、今回の件で「セキュリティの重要性」と「AWSの奥深さ」を学び、少しだけレベルアップできた気がします。
皆さんも、ボットの影を感じたら早めの対策を!
最後に、エコモットではソフトウェアエンジニアを募集しています!
IoTやWeb開発に興味のある方はぜひ、弊社採用ページもご覧ください。



