Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Your Next.js API route is public—even if your UI isn’t.

2026年09月29日に「Dev.to」が公開したITニュース「Your Next.js API route is public—even if your UI isn’t.」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Next.jsのAPIルートは、UIが隠れても外部から直接呼び出される危険性がある。APIルート自身がCAPTCHAなどでリクエストを検証する仕組みが必要だ。FCaptchaを使ったNext.jsのAPIルートハンドラでのサーバーサイド検証の実装方法を解説する。秘密鍵はサーバーで扱い、サーバー間で検証を行うのが重要だ。

ITニュース解説

Webアプリケーションを開発する際、ユーザーインターフェース(UI)上に表示されるフォームを、例えば「ログインしているユーザーにだけ表示する」といった条件で非表示にすることがよくある。しかし、このUIの表示条件を操作するだけでは、そのフォームがデータを送信する先のAPIルートは「公開状態」のままであるという重要な点がある。これは、Web開発、特にセキュリティを考える上で、システムエンジニアを目指す人々が最初に理解すべき落とし穴の一つである。

具体的には、ウェブサイトの見た目からはフォームが見えなくても、その裏側にあるデータ処理用のAPI(例えば、お問い合わせ内容をサーバーに送るAPIなど)は、誰でも直接アクセスして呼び出すことができてしまう。もしこのAPIルートが、独自の厳密なチェックや検証を行わない場合、悪意のあるユーザーやプログラムが、ウェブサイトのUIを一切使わずに、直接APIを叩いて不正な操作(例えば、大量のスパムメッセージを送信したり、データベースに不要なデータを書き込んだりする行為)を行ってしまう可能性があるのだ。たとえ、アカウント登録が不要な公開のお問い合わせフォームであっても、サーバー側で受信したリクエストが正当なものかどうかを検証することは極めて重要となる。この検証プロセスがなければ、不正なリクエストが意図しないメール送信やデータベース操作を引き起こすリスクがある。

この記事では、Next.jsという人気のWebフレームワークのApp Router機能を使って作成されたAPIルートを保護するための具体的な方法が示されている。その解決策として、FCaptchaというセルフホスト型のCAPTCHAサービスが利用される。FCaptchaは、リクエストを送っているのが人間であるか、それとも機械(ボット)であるかを判断するための仕組みだ。基本的な流れはこうだ。まず、ユーザーのブラウザがFCaptchaサービスから「トークン」と呼ばれる一時的な認証情報を取得する。このトークンは、そのリクエストが人間によって行われた可能性が高いことを示すものだ。次に、ブラウザはこのトークンを、フォームの入力内容(例えばメッセージ)と一緒にサーバー側のAPIルートに送信する。最後に、サーバー側のAPIルートは、受け取ったトークンが本当に有効で、不正なものではないかをFCaptchaサービスに問い合わせて検証する。この検証に成功した場合のみ、サーバーはリクエストを受け入れ、メール送信やデータベース書き込みなどの本来の処理を実行する。

提供されているサンプルコードは、このようなセキュリティ対策がどのように機能するかを実際に体験できるように設計されている。開発者はGitを使ってソースコードをダウンロードし、必要なツールをインストールした後、Next.jsアプリケーションとFCaptchaサービスをローカル環境で起動できる。これにより、ブラウザ(クライアント)とサーバーがどのように連携してセキュリティを確保しているのかを、コードと実際の動作を通じて確認することが可能になる。

このシステムでは、ブラウザとサーバーの役割が明確に分かれている。ブラウザ側では、Next.jsのpage.jsというファイルがクライアントサイドのフォームを表示し、ユーザーの操作に応じてshared/browser.jsというヘルパー関数を呼び出す。このヘルパー関数は、FCaptchaウィジェットを画面に表示し、特定のアクション(例えば「お問い合わせ」)のためにトークンを要求する。FCaptchaからトークンが発行されると、ヘルパー関数はそのトークンとユーザーが入力したメッセージをまとめて、サーバー側の/api/contactというAPIルートに送信する。ここで非常に重要なのは、FCaptchaの検証に必要な「秘密鍵」は、ブラウザ側には一切渡されないということだ。この秘密鍵はサーバー側でのみ保持され、クライアントには絶対に公開してはならない情報である。

一方、サーバー側のAPIルートは、Next.jsのApp Routerにおけるnextjs/app/api/contact/route.jsというファイルと、共通の検証ロジックを持つshared/contact.mjsというファイルで実装されている。ブラウザから/api/contactへPOSTリクエストが来ると、route.jsがこれを処理し、contactHandlerという共有ハンドラー関数を呼び出す。この関数には、環境変数から取得されたオリジン情報、FCaptchaサービスのURL、そして最も重要なFCaptchaの「検証用秘密鍵」が渡される。このverifySecretという秘密鍵は、process.env.FCAPTCHA_VERIFY_SECRETのように環境変数としてサーバー側でのみ読み込まれ、NEXT_PUBLIC_といったプレフィックスを付けてクライアント側からアクセスできるようにしてはならない。これは、秘密鍵が不正に入手されるのを防ぐための基本的なセキュリティ対策だ。

サーバー側のルートハンドラーでは、まず受け取ったリクエストに対する厳密な初期チェックが行われる。具体的には、リクエストが期待されるOriginヘッダー(どこから来たリクエストか)、application/jsonという正しいコンテンツタイプを持っているか、リクエストのボディサイズが制限(例えば16 KiB)を超えていないか、メッセージが空ではないか、そして適切な長さ(例えば2,000文字以内)であるか、さらにはFCaptchaトークンが存在するかどうかを確認する。これらの基本的なチェックをクリアした場合にのみ、サーバーは次のステップに進む。

次に、サーバーは受け取ったFCaptchaトークンが正当なものかを検証するために、FCaptchaサービスのエンドポイント(/siteverify)に対して、サーバー間で直接HTTPリクエストを送信する。このとき、サーバーは自分自身が安全に管理している「検証用秘密鍵」と、クライアントから受け取ったトークンをFCaptchaサービスに送る。FCaptchaサービスは、これらの情報を使ってトークンの正当性を確認し、その結果をサーバーに返す。サーバーはFCaptchaサービスからの応答を精査し、トークンが本当に有効であるか、リクエストが正しいホスト名から送られたか、そして期待されるアクション('contact')のために発行されたトークンであるかを確認する。もし、検証が失敗した場合(例えば、トークンが無効である、過去に一度使われたトークンである(リプレイアタック防止のため)、ホスト名やアクションが一致しないなど)、APIルートは即座にエラー応答(例えば、ステータスコード403 Forbidden)を返し、メッセージの保存やメール送信といった保護されたアクションは一切実行されない。検証が成功した場合にのみ、安心して次の処理に進むことができる。

エラー発生時の対応も非常に重要である。ネットワークの問題やFCaptchaサービスからの不正な応答があった場合は、サービスが一時的に利用できないことを示す503エラーを返す。トークンが拒否された場合は403エラー、不正な入力データだった場合は400エラーを返す。重要なのは、検証に失敗したり、タイムアウトしたりした場合でも、決して「成功した」と偽って処理を進めてはならないということだ。このような場合は、ユーザーにエラーを明確に示し、新しいトークンで再度試行するよう促すのが正しい対応である。この記事では、curlコマンドを使って、偽のトークンでAPIルートに直接リクエストを送った場合でも、サーバーが正しく403エラーを返すことを確認できる例も示されている。

本番環境にアプリケーションをデプロイする前には、いくつかの重要な注意点がある。ローカル環境で動作するサンプルコードは、毎回新しい一時的な秘密鍵を生成し、FCaptchaサービスもローカルのループバックアドレスで起動するようになっている。これを本番環境で使う場合は、必ずHTTPSで保護された公開URLと、安定して管理される本番用のサイトキー、そしてサーバー側で安全に保管される秘密鍵に置き換える必要がある。また、FCaptchaは不正なボットからの攻撃を防ぐための強力なツールだが、それは数あるセキュリティ対策の一つに過ぎない。レート制限(特定のユーザーやIPアドレスからのリクエスト回数を制限する)、リクエストのサイズ制限、そして認証や認可(ユーザーが誰であるかを識別し、そのユーザーが特定のアクションを実行する権限を持っているかを判断する)といった、他のセキュリティ対策も必要に応じて必ず併用すべきだ。Originヘッダーのチェックは、ブラウザからの不正なリクエストに対してはある程度の効果があるが、悪意のあるスクリプトは簡単にこのヘッダーを偽装できるため、これも完全な防御策ではない。

さらに、フォームの内容をデータベースに保存したり、メールを送信したりといった「ビジネスアクション」を追加する際には、重複した処理が行われないよう注意が必要だ。FCaptchaのトークンは一度使用されると消費されるため、もしデータベース書き込みやメール送信の処理が失敗して再試行が必要になった場合、新しいFCaptchaトークンを再取得する必要がある。もし複数のFCaptchaサーバー(レプリカ)を使用するような大規模なシステムを構築する場合は、リプレイアタックを防ぐために、これらのサーバー間でトークンの使用状況を共有する仕組みも考慮する必要があるだろう。正当なユーザーが何らかの理由でCAPTCHA検証に失敗してしまった場合でも、彼らが問題を解決し、最終的に目的を達成できるような「リカバリーパス」を提供することも、ユーザーエクスペリエンスの観点から重要となる。

このデモアプリケーション自体は、実際にメッセージを保存したりメールを送信したりする機能は持たず、あくまでFCaptchaによる検証ロジックを実演するために作られている。FCaptchaは無料でオープンソースのプロジェクトであるため、開発者は自由に利用し、改善に貢献することもできる。システムエンジニアを目指す初心者がこのような具体的な実装例を通じて、Webアプリケーションのセキュリティと、クライアント・サーバー間の連携の重要性を学ぶことは、将来のキャリアにおいて非常に役立つ知識となるだろう。

関連コンテンツ

関連IT用語

関連ITニュース