LLMチャットを​守る​4層 ──レート制限・Turnstile・WAF・LLMスパム判定

AIエージェントを公開すると、従来のWebフォームには無かった攻撃面が増える──「リクエストを1回叩くたびにLLMの請求が発生する」。御用聞きAIをローンチ前に守るために実装した、レート制限(AIコスト防衛)・Turnstile・エッジWAF・LLMによるスパム判定の4層防御を、実装ベースで解説する。鍵は、層を重ねること(defense in depth)と、安全側の“向き”を層ごとに使い分けること──コストは迷ったら止め、見込み客は迷ったら通す。

甲斐ショウジ甲斐ショウジ
CAG主宰/合同会社ATK CAIO(最高AI責任者)
技術10分で読めます
技術LLMチャットを守る4層 ──レート制限・Turnstile・WAF・LLMスパム判定

技術ノート | 作ったものを、運用に耐えるまで作り切る

AIエージェントを世に出すとき、機能を作り切るのと同じくらい重要なのが、公開した瞬間に始まるbotとスパムへの備えだ。しかもLLMを組み込んだプロダクトには、従来のWebフォームには無かった攻撃面が一つ増える——リクエストを1回処理するたびに、LLMの利用料金が発生する。つまり「壊される」前に「請求が爆発する」。

電脳技巧集団(AI職人ギルド)が開発する顧客対応AI「御用聞きAI」は、問い合わせ受信 → AIの一次対応 → 顧客専用ダッシュボード → 継続発注までを自動で回すSaaSだ。これを正式ローンチする前に、被害が出てから慌てるのではなく、予防として多層の防御を入れた。本記事はその実装記録——「設定を一つ入れて終わり」ではなく、どの層で・何を・なぜそう判断したかを、実際に書いたコードと設定の単位で残す。

結論を先に言う。防御は4層で重ねた。①アプリ層のレート制限(AIコスト防衛)/②Turnstile(人間性の確認)/③エッジWAF(流量の遮断)/④LLMによるスパム判定(受信メール)。そして4層を貫く設計原則が2つある——「多層で重ねる」と「安全側の向きを層によって逆にする」。

ダークテーマのセキュリティ管制画面。中央のAIチャット応答エンドポイントに向かう大量のリクエストを、手前から RATE LIMIT / TURNSTILE / WAF / SPAM FILTER と書かれた4枚の半透明の盾が順に受け止めている多層防御のイメージ
AIチャットへ向かう攻撃を、レート制限・Turnstile・WAF・スパム判定の4枚の盾で順に受け止める
顧客対応AIの制作事例記事のサムネイル 関連記事 | 本記事で守っているプロダクトの中身このサイトのAIは、自分たちで作った ──HITL・顧客別メモリ・双方向を実装した顧客対応AIの中身

01まず攻撃面を​"1回いくらか"で​棚卸しする

防御を足す前に、「どの入口が、誰でも叩けるか」を一覧にした。守るべきは公開された口であって、社内向けや署名付きの口ではない。

  • 公開問い合わせフォーム(誰でもPOSTできる)
  • 埋め込みウィジェットAPI(外部サイトからのサーバー間連携・キー+Origin照合あり)
  • AIチャットの応答エンドポイント(ログイン済みクライアント)=ここがLLMを呼ぶ=1回ごとにコストが出る
  • 受信メール(専用ドメイン宛・botや営業メールが"問い合わせ"として流入し得る)
  • ログインリンク送信(乱用でメール爆撃になり得る)

この棚卸しで分かったのは、最もコストリスクが高いのに無防備だったのがAIチャットの応答エンドポイントだったこと。フォームやログインには既にレート制限とハニーポットがあったが、LLMを呼ぶ口だけが素通しだった。攻撃面は「壊れるか」だけでなく、"1回いくらか"で評価しないといけない。

公開された入口 = 守るべき攻撃面 公開フォーム ウィジェットAPI ログインリンク送信 受信メール AIチャット応答エンドポイント1リクエスト = LLM推論 = 課金が発生=最もコストリスクが高いのに、ここだけ無防備だった ¥
同じ"入口"でも、AIチャットだけは1回叩かれるごとにLLM課金が発生する。攻撃面はコストで捉え直す

02第1層:レート制限 — AIコスト爆発を​止める

AIチャットの応答は、1リクエストでLLMの推論が走る=1回ごとに課金される。ここにレート制限が無いと、トークンが漏れたり一人のクライアントが連打したりするだけで、請求が青天井になる。普通のWebの「DoSで落ちる」とは別種の、財務的なDoSだ。

そこでAI応答エンドポイントに、クライアント単位の二段のレート制限を入れた。短期(直近の一定時間に数十回まで)と日次上限。短期は連打を止め、日次上限は「じわじわ叩き続ける」攻撃のコスト天井を作る。重要なのは、メッセージを保存しLLMを呼ぶ"前"でガードすること——超過したリクエストはDBにも書かず、推論にも入らず、即座に拒否する。守るのは「処理結果」ではなく処理が始まる手前だ。

記録はサーバーレスでも効くようDBベースで持つ(インメモリはインスタンスをまたぐと効かない)。公開フォームやログイン再送にも、IP単位・メール単位で同じ仕組みを掛けている。「1分に数十回も問い合わせる人間はいない」前提で、通常利用には当たらず機械的な連打だけを止める閾値にする。

連打は LLM の手前で止める = 課金させない 大量リクエスト レート制限短期+日次上限超過は429 正規の1件だけ通過 LLM 推論=ここで課金 コスト:低いまま
連打はLLM推論の手前で弾く。処理が始まる前に止めるから、攻撃が来てもコストはほぼ増えない

03第2層:Turnstile — ​人間性を​フォームで​確かめる

レート制限は"量"を止めるが、"そもそもbotか人間か"は判定しない。そこで公開フォームには Cloudflare Turnstile(CAPTCHAの後継・多くの訪問者には何も見せず裏で検証する方式)を入れた。送信時にクライアントがトークンを取得し、サーバー側でそのトークンを検証してから受け付ける。

実装で効かせた設計判断が一つある。「サイトキーとシークレットの両方が設定されているときだけ有効化する」ことだ。地味だが重要で、サーバーのシークレットだけが設定された状態だと、クライアントはトークンを発行できないのにサーバーは全送信を弾く——設定ミスでフォームが全滅する。両方揃ったときだけ検証する設計にしておけば、鍵が無い環境(プレビューや開発)では自動でスキップされフォームは普通に動く。"安全機構が原因で本番が壊れる"事故を、コードの構造で防いでおく。

ダークテーマの問い合わせフォーム。名前・メール・内容の入力欄の下に『私はロボットではありません』のチェック済みウィジェットが緑のチェックで成功表示され、送信ボタンが有効になっている画面
公開フォームの送信前に人間性を確認。大半の訪問者には負担をかけず、裏で静かに検証する

このTurnstileは既存の自社サイトと同じウィジェット・同じ鍵を共用した。共用にすると許可ドメインを一箇所で管理できる。なおこの作業中に、自社サイトの本番ドメインがTurnstileの許可ホストに入っていなかった穴も同時に塞いだ(詳細は後述の落とし穴で)。

04第3層:WAF — エッジで​流量を​止める

アプリ層のレート制限は強力だが、関数が起動してから効く。攻撃が大量に来ると、弾く処理自体にサーバーレス関数の起動コストがかかる。そこでもう一段、関数が走る手前=エッジ(Vercel WAF)でレート制限を掛けた。

公開APIのパス(問い合わせ・ウィジェット・AIチャット・受注フォーム)にIP単位の流量ルールを設定し、超過は即座に遮断する。これで攻撃トラフィックは関数に到達する前に落ち、コストと負荷の両方を削れる。プラットフォームの自動DDoS緩和は元々常時オンなので[1]、その上に「自分たちのAPI形状に合わせた」ルールを重ねる形だ。

ここでの設計判断は「webhookとバックグラウンド連携のパスは除外する」こと。受信メールのwebhookやジョブ基盤は、外部サービスが正当に短時間で大量送信する。ここに素朴なIPレート制限を掛けると、連携そのものを壊す。"公開フォーム/チャットの口だけ"を対象にし、機械が正当に叩く口は外す——何を守り、何を守らないかを明示的に決めるのがWAF設計の肝だ。

エッジ(関数の手前)で流量を遮断 = 関数コストを発生させない 攻撃流量 WAFエッジIP流量 関数 + アプリ層キー単位の精緻な制限 正規リクエストは通過 webhook / 連携 = WAF対象外(正当なバーストを壊さない)
攻撃流量はエッジで遮断、正規リクエストは通過、webhook/連携は対象外。二段で重ねつつ正当な機械送信は壊さない

05第4層:LLMスパム判定 — 受信メールを​LLM自身で​仕分ける

御用聞きAIは、専用ドメイン宛のメールも"問い合わせ"として取り込む。ここはCAPTCHAを置けない入口だ——botや営業メール、フィッシングが、そのまま問い合わせ一覧を埋める

この層にはLLM(軽量モデル)を使った。受信メールを種別判定・要約するついでに、「これはスパムか」を同じ一回の推論で判定させる。スパムと判定されたメールは、消さずに"隔離"し、スタッフ通知だけを抑制する。完全に捨てない理由は2つ。監査できることと、誤判定からの復旧ができることだ。サイレントに削除すると、本物の問い合わせを1通でも誤って捨てたとき誰も気づけない。隔離なら一覧から消えても後から拾える。

ダークテーマの管理画面の受信トレイ。正当な問い合わせ2件が明るく未読表示される一方、営業・宣伝らしき1件に黄色の『QUARANTINED(隔離)』タグが付き、通知されず別扱いになっている画面
LLMがスパムを隔離。一覧とスタッフ通知は汚さず、消さないので誤判定でも後から復旧できる

判定プロンプトには明確な"向き"を持たせた——迷ったらスパムではない(通す)側に倒す。理由は次章の設計原則につながる。

064層を​貫く​2つの​設計原則

多層で重ねる(defense in depth)。4層は役割が違う。Turnstile=人間性/WAF=エッジの流量/アプリ層=精緻なキー単位/LLM=メールの内容判定。どれか一つで全部を止めようとしない。1層が抜けても次が拾う、重なりで守る。攻撃面ごとに最適な層が違うから、束で掛ける。

安全側の"向き"を、層によって逆にする。これが最大の設計判断だ。「迷ったときにどちらへ倒すか」は、層によって正反対になる。

「迷ったらどちらへ倒すか」は、何を失うと痛いかで決める コスト系迷ったら止める 誤って弾いても、数秒待てば再送できる 見込み客系迷ったら通す 誤って捨てたら、見込み客は戻らない コストは取り返せる。機会は取り返せない。だから向きが逆になる
同じ「安全側に倒す」でも向きは逆。コスト系(レート制限・WAF)は迷ったら止める、スパム判定は迷ったら通す
  • AIコスト系(レート制限・WAF)=迷ったら止める。誤って数リクエストを弾いても、ユーザーは少し待てば再送できる。失うのは数秒。守るのは青天井の請求。
  • スパム判定=迷ったら通す。正当な問い合わせを1通でも誤って捨てたら、失うのは見込み客そのもの。だから安全側は"通す"で、しかも隔離(復旧可能)にする。

コストは取り返せるが、機会は取り返せない。この非対称を意識して各層のデフォルトを決めた。あわせて徹底したのが「サイレントな失敗を作らない」こと。弾いたとき・隔離したときは必ず記録に残し、後から「なぜ弾いたか」を追えるようにする。静かに失敗する防御は、効いているのか壊れているのか分からない——それは防御していないのと同じだ。

07実装の​落とし穴​(実際に​踏んだ​もの)

机上で「CAPTCHAを入れる」と決めるのと、実装してエラーコードを実際に見るのは別の作業だ。今回踏んだ罠を残しておく。

  • Turnstileは登録ドメイン(apex)を自動カバーしない。www.example.com は手入力で追加できるのに、example.com(apex)は手入力では追加されない。「Cloudflare上の既存サイト(ゾーン)から選択」で明示的に許可する必要があり、許可外のドメインでは検証が 110200(ホスト名不許可)で失敗する[2]。反映には数分かかるので直後のエラーで焦らない。
  • WAFはwebhook/連携パスを除外する。ここを忘れると正当な連携が壊れる。
  • 書き込み専用のシークレットは読み戻せない。"sensitive"で保存した鍵は後からAPIでも値を取得できない(それが正しい設計)。別環境へ展開するときは発行元から取り直す前提で運用する。
  • ビルド時に焼き込まれる公開設定は、変更後に必ず再デプロイ。クライアントに渡る設定値はビルド時に埋め込まれるため、設定だけ変えても再ビルドしないと反映されない。

どれも「ドキュメントに小さく書いてあるが、踏むまで気づかない」種類の罠だ。実装してエラーコードを実際に見て、初めて設計が固まる——これは魔法ではなく、地に足のついた検証の積み重ねだ。

AIエージェントの防御は、「壊れるか」だけでなく「1回いくらか」で攻撃面を捉え直すところから始まる。

観点無防備なAIエージェントCAG の4層防御
AIチャット叩かれ放題=LLM請求が青天井レート制限(短期+日次上限・推論の手前で遮断)
公開フォームbotが自由に送信Turnstile+ハニーポット+レート制限
流量・DoS関数が起動してから対処エッジWAFで関数の手前に遮断
受信メールスパムが問い合わせ一覧を埋めるLLMが判定し隔離(消さず復旧可)
誤判定時サイレントに失敗・気づけない記録に残し追跡可・安全側の向きを層で設計
設計思想一つの対策で済ます多層で重ね、何を守り何を守らないかを明示

※ 本記事は御用聞きAI(CAGの自社プロダクト)の実装に基づく一次情報。レート制限の具体閾値・鍵・内部IDは安全のため非掲載。各プラットフォームの仕様(自動DDoS・Turnstile挙動等)は公式ドキュメントに基づく(v0・2026-06-16時点。仕様は更新されうる)。

脚注・出典

  1. Vercel は全プロジェクトで自動DDoS緩和を常時提供。その上に WAF のカスタムルール(レート制限等)を重ねられる。Vercel公式ドキュメント(Vercel Firewall / WAF)。vercel.com/docs/vercel-firewall
  2. Cloudflare Turnstile はウィジェットの許可ホスト名でリクエスト元を検証し、許可外ホストではエラーコード 110200 を返す。登録ドメインの扱いは Cloudflare 公式ドキュメント参照。developers.cloudflare.com/turnstile
  3. 本記事のレート制限・Turnstile・WAF・LLMスパム判定の設計と実装は、電脳技巧集団が御用聞きAI向けに行った自社実装に基づく(2026-06-16時点・v0)。閾値や鍵等の具体値はセキュリティ上非掲載。

「作る」だけでなく​「公開して​運用に​耐える」まで​作り切る。

AIエージェントの設計・実装から、コスト防衛・bot/スパム対策・運用までを一気通貫で。最新AIを、現場で本当に回る仕組みとして設計します。まずは相談から──問い合わせは、AIがその場でお応えします。

無料で相談する →

言語化できるものは、全て作る。

あなたの「作りたい」を、定価とスピードで形に。まずは無料の相談から。

制作事例を見る