Notes / Web技術

hidden inputで公開されるAPIキーのabuse制御。秘密鍵と公開識別子の違い

フォームのhidden inputにAPIキーを入れるSaaS(Web3Forms等)は珍しくありません。「鍵が公開されている」とAIレビューが指摘してくることもありますが、これは秘密鍵漏洩リスクではなく abuse(乱用)制御の話。識別子としての公開鍵と、防御策の整理。

security API key abuse control form

フォームのhidden inputにAPIキーを入れる構成は、静的サイトとサーバーレス系のSaaSでよく見ます(Web3Forms等)。

<form action="https://api.web3forms.com/submit" method="POST">
  <input type="hidden" name="access_key" value="abc-123-xyz" />
  <!-- 入力欄 -->
</form>

「access_keyが公開されている、秘密鍵漏洩リスク」。AIレビューがこう指摘してくることがあります。半分正しく、半分誤解。これは秘密鍵漏洩リスクではなく、abuse (乱用)制御の話。対策の軸が違います。

秘密鍵と公開識別子は別物

API認証で使われる「キー」には大きく2種類あります。

種類公開可否漏れた時のリスク
秘密鍵(secret)AWS Secret Access Key、Stripe secret_key、GitHub Personal Access Token絶対に公開不可直接の不正利用、データ流出、課金被害
公開識別子(public identifier)Web3Forms access_key、Stripe publishable_key、Google Maps API key (Web用)公開前提abuse (スパム送信、不正リクエスト)

両者は名前が似ていますが、設計思想が違います。秘密鍵は「持っていれば全権限が使える鍵」、公開識別子は「サービスのどのエンドポイントか識別するためのラベル」。

公開識別子は仕様上「サーバーには出ない、クライアントに出る」のが前提。フォームのhidden inputに書くのは仕様通り。これを「秘密鍵が漏れている」と判断するのは混同です。

なおGoogle Maps API keyのような「Web用に公開される鍵」も同様で、HTMLから見えるのは想定通り。ただしHTTP referrer制限、利用APIの制限、課金上限をSaaS側で必ず設定する前提です。「公開前提」と「無制限に使ってよい」は別の話。

公開識別子のリスクはabuse

公開識別子は誰でも見られる前提。悪意ある第三者がそれを使って不正リクエストを送るリスクがあります。スパムフォーム送信を大量に飛ばす。別ドメインから自サイト経由のフリをして送る。レート制限を超えるリクエストで通知先(運用者のメール)を埋める。課金型なら見えない請求を発生させる。

これらはabuse (乱用)と呼ばれる類で、秘密鍵漏洩とは対処法が違います。

abuse制御の対策

公開識別子を使うSaaSがabuse対策として提供するのは、主にドメイン制限、CAPTCHA、レート制限の組み合わせです。

Domain allowlist (origin、refererの制限)

「このキーはhttps://example.comからのリクエストのみ受け付ける」とSaaS側で設定します。第三者が自サイトで同access_keyを使ったフォームを置いても、SaaSがrefererやoriginを見て弾きます。設定箇所はSaaS管理画面のAllowed OriginsかAllowed Domains。

ただしブラウザフォーム経由の乱用は抑えられても、curl等の非ブラウザクライアントはHTTPヘッダーを偽装できる。暗号学的な認証ではないことを理解しておきます。あくまで「通常のフォーム経由の流用を抑える」ための仕組みです。

CAPTCHA、Bot検証

reCAPTCHA、Cloudflare Turnstile、hCaptcha等をフォームに組み込み、自動化されたbot送信を弾きます。ハニーポット欄(<input type="text" name="botcheck" style="display:none">)は最低限の対策で、botが誤って入力するダミー欄として機能します。Turnstile (Cloudflare無料)は現代の標準、UXへの影響が最小。設定はSaaSの管理画面、もしくはフォーム側で組み込みます。

レートリミット

「同IPから1分間にX回まで」「同access_keyで1日Y回まで」のような制限をSaaS側で設定します。乱用が始まっても影響範囲を限定できる。設定箇所はSaaS管理画面のRate LimitsかQuota。上限近づき時にemailやSlack通知を設定すると気づきやすいです。

実装側で隠す、は対策にならない

「access_keyを直書きするのが心配なら、ビルド時に環境変数から読む」という提案もよく見ます。けれどabuse対策にはなりません。

<!-- ビルド時 -->
<input type="hidden" name="access_key" value={import.meta.env.PUBLIC_KEY} />

結局HTMLとして配信されるので、ブラウザの「ソース表示」で誰でも見られます。環境変数を経由しても、最終配信物に書かれている事実は変わらない。

公開識別子は「ソースを見られる」前提で設計されたもの。隠すのではなくabuseを制御する設計にします。

AIレビューが「鍵漏洩」と指摘してきたら

実例として「WEB3FORMS_ACCESS_KEY がhidden inputとして公開される。キー流用によるスパム送信、フォーム濫用のリスク」という指摘がありました。

これも半分正しく半分誤解。スパム送信リスクは存在するので、abuse対策は必要です。けれど「秘密鍵としての漏洩」ではなく、設計上公開される識別子。反論の正しい軸は「秘密鍵ではないので隠しようがない、abuse対策でSaaS側設定を確認する」。AIレビューの優先度ラベル(例: P1 Security)は誇張気味で、Domain allowlist、CAPTCHA、レートリミットの運用設定確認に変えます。


「key」と名のつくものが全部秘密ではありません。公開識別子と秘密鍵を区別し、公開識別子は「ソースに見える前提」でabuse制御を組む。ドメイン制限、CAPTCHA、レートリミットをSaaS側で設定すれば、フォームのhidden inputにaccess_keyを書いていても乱用リスクを下げられます。

公開識別子の扱いを含むフォーム周りのセキュリティ確認は、お問い合わせから相談できます。