APIキーとシークレットの漏洩とは?起きるパターンと対策法

APIキーとシークレットの漏洩とは?起きるパターンと対策法

#セキュリティ#Web開発#脆弱性対策

APIキーやシークレットの漏洩は、コード、ビルド成果物、ログ、ストレージなどに認証情報が残り、外部や権限のない利用者に露出する問題です。 対策の勘所は、秘密情報をコードの外で管理し、漏れても即座に失効できる運用にできているかどうかです。

APIキーとシークレットの漏洩とは

シークレット漏洩:APIキー、トークン、パスワード、秘密鍵などの認証情報が、公開すべきでない場所に置かれて第三者に取得される問題です。 ハードコードだけでなく、ログ出力、公開設定、CIやビルドへの受け渡しなどを通じて、秘密情報が権限のない主体から到達できる場所に残ることが根本原因になります。

漏れた鍵は、そのサービスの権限をそのまま攻撃者に渡します。 外部APIの不正利用による高額請求、クラウド資源の乗っ取り、鍵で保護されたデータへの直接アクセスに至ります。 コードの脆弱性と違い、正しく動くコードでも鍵が1つ露出しただけで成立するため、気づきにくく被害が広がりやすいのが特徴です。

主な漏洩経路は次のとおりです。

  • Gitへのコミット:設定ファイルやコード内の鍵をそのままコミットし、公開リポジトリや履歴から取得される。公開リポジトリは自動化されたボットに常時走査されている
  • フロントエンドのバンドル埋め込み:秘密鍵をクライアント側のコードに含め、配信されたJavaScriptから読まれる。ブラウザに届く時点で秘密ではなくなる
  • 公開ストレージ:バケットやログ、バックアップを誤って公開設定にし、そこに含まれる鍵が露出する

APIキーとシークレットの漏洩が起きるパターン

典型的な弱点は次のとおりです。

  • 認証情報をソースコードや設定ファイルに直書きし、そのままコミットしている
  • 一度コミットした鍵を後から消しても、Gitの履歴に残り取得できてしまう
  • サーバ側で使うべき秘密鍵を、フロントエンドの環境変数に入れてバンドルに含めている
  • ログやエラー通知、監視ツールへの送信データに認証情報を出力し、収集基盤経由で広く読まれる
  • CIの設定ファイルやDockerイメージのレイヤに鍵を埋め込み、成果物から抽出される
  • 漏洩に気づいても、鍵を失効させ再発行する手順が整っておらず、露出が続く

共通するのは「秘密情報がコードや成果物に混ざり、外部から到達できる場所に残っている」という点です。

対策法

原則は「秘密情報をコードの外に置き、漏れた場合に即座に無効化できるようにする」ことです。

  • 認証情報は環境変数やシークレット管理サービスで扱い、コードやリポジトリに書かない。これが本命の対策になる
  • コミット前後でシークレットスキャンを回し、鍵の混入を自動で検知する(pre-commitフックとCIの両方)
  • フロントエンドには公開してよい値だけを渡し、秘密鍵はサーバ側にとどめる
  • 鍵の失効とローテーションの手順を整え、漏洩時にすぐ再発行できるようにする
  • 万一に備え、鍵ごとの権限と有効範囲を最小化し、被害範囲を狭める(被害軽減策)

コミット後に履歴から鍵を消すのは手間がかかるうえ取り込まれた後では手遅れになりやすいため、混入させない仕組みを先に入れるのが最優先です。 シークレットスキャンとコード外管理を土台にし、失効とローテーションの手順を整えるのが費用対効果の高い順序になります。

React(TypeScript)での事例

フロントエンドで最も多い事故は、「環境変数に置いたから安全」という誤解です。

// 危険:VITE_ プレフィックスは「ビルド成果物に埋め込んでよい」という宣言
const res = await fetch("https://api.example-ai.com/v1/generate", {
  headers: { Authorization: `Bearer ${import.meta.env.VITE_AI_API_KEY}` },
});

Vite の VITE_、Next.js の NEXT_PUBLIC_、CRA の REACT_APP_ プレフィックスが付いた変数は、ビルド時にクライアント用 JavaScript へ文字列として埋め込まれます。 .env ファイルに置いてあっても、配信されたバンドルを開けば誰でも読める点は、コードに直書きした場合と変わりません。 ブラウザに届いた時点で、それはもう秘密ではなくなります。

外部 API の秘密鍵を使う処理は、自前のバックエンドを経由させます。

// フロントは自分のサーバだけを呼ぶ。鍵はサーバ側にとどめる
const res = await fetch("/api/generate", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ prompt }),
});

中継エンドポイント側では、認証(誰が呼べるか)とレート制限を忘れずに付けます。 中継が無認証だと、鍵は隠せても「他人の課金で API を叩ける口」が公開されたままになります。

Rails での事例

Rails 側の定番の置き場所は credentials です。 暗号化されたファイル(config/credentials.yml.enc)はリポジトリにコミットし、復号鍵(config/master.key)だけを Git 管理外に置きます。

bin/rails credentials:edit
ai_api_key: "sk-..."
# コードからは credentials 経由で参照する
client = ExampleAi::Client.new(api_key: Rails.application.credentials.ai_api_key)

master.key は新規アプリなら生成時に .gitignore へ登録されますが、既存アプリでは登録されているか必ず確認します。 OSや実行基盤から環境変数を注入する方式では、.envファイルを使う必要はありません。 dotenvを使ってローカルの.envから読み込む場合は、.env.gitignoreに登録します。 ただし、.gitignoreはDockerのビルドコンテキストを除外しないため、.dockerignoreにも.envを登録します。 イメージのビルド中だけ必要な鍵はBuildKitのsecret mountで渡し、実行中のアプリが使う鍵はデプロイ基盤の実行時シークレット機能から注入します。 BuildKitのsecret mountはビルド命令の実行中だけ利用でき、実行中のコンテナへ鍵を渡す仕組みではありません。

あわせて、ログへの流出も塞ぎます。 リクエストパラメータにトークンが乗る経路があるなら、filter_parameters でログ出力をマスクします。

# config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [
  :password, :token, :api_key, :secret
]

コード、リポジトリ、ビルド成果物、ログのどこにも平文の鍵が残らない状態が目指す形です。

まとめ

APIキーとシークレットの漏洩対策の要点を整理します。

  • 根本原因は「秘密情報がコードや成果物に混ざり、外部から到達できる場所に残っていること」。正しく動くコードでも、鍵が1つ露出すれば権限がそのまま攻撃者に渡る
  • 本命の対策はコード外での管理。 Railsならcredentials(暗号化ファイルをコミットし、master.keyだけをGit管理外に置く)を使える。 環境変数は実行基盤から直接注入でき、dotenvで.envを使う場合に限って.gitignoreへの登録が必要になる
  • Dockerでは.gitignoreとは別に.dockerignoreで秘密情報をビルドコンテキストから除外する。ビルド時の鍵にはBuildKitのsecret mount、実行時の鍵にはデプロイ基盤のシークレット機能を使い分ける
  • 混入を検知する仕組みを先に入れる。 コミット後に履歴から鍵を消すのは手間がかかるうえ、公開リポジトリは自動化されたボットに常時走査されている。 pre-commitフックとCIの両方でシークレットスキャンを回す
  • フロントエンドの環境変数は秘密の置き場所ではない。VITE_NEXT_PUBLIC_REACT_APP_が付いた値はビルド成果物に文字列として埋め込まれ、バンドルを開けば誰でも読める
  • 外部APIの鍵を使う処理は自前のバックエンド経由にする。中継エンドポイントには認証とレート制限を付ける(無認証だと「他人の課金でAPIを叩ける口」が残る)
  • ログへの流出も塞ぐ。filter_parametersにトークンやパスワードを登録する
  • 鍵ごとの権限と有効範囲を最小化し、失効とローテーションの手順を整えておく(被害軽減策)

目指す形は、コード・リポジトリ・ビルド成果物・ログのどこにも平文の鍵が残らない状態です。 既存リポジトリの点検は、履歴を含めたシークレットスキャンから始めてください。

関連して読める記事

鍵が置かれる場所と漏れ方は、設定管理の問題と地続きです。 公開設定のままのバケットやログ基盤は、セキュリティミスコンフィグの典型例でもあります。 APIのレスポンスにapi_tokenのようなカラムが載って持ち出されるケースは過剰なデータ露出として起き、クラウドのメタデータエンドポイントから一時認証情報を奪われる経路はSSRFの主要な被害です。

セキュリティミスコンフィグとは?起きるパターンと対策法

セキュリティミスコンフィグの仕組みと起きるパターン、安全な既定への統一と環境をまたいだ設定管理を軸にした対策を整理します。Sidekiq の Web UI を認証なしでマウントする危険例と Devise の authenticate で囲む方法、本番で consider_all_requests_local が true になっている場合の露出を具体的なコードで示します。

2026年7月25日

過剰なデータ露出(Excessive Data Exposure)とは?起きるパターンと対策法

過剰なデータ露出の仕組みと起きるパターン、シリアライザ層で返す属性を列挙する対策を整理します。render json: @user が password_digest まで返す危険例、filter_attributes や Devise では守れない範囲、関連モデルの include による芋づる露出、本人と他人でシリアライズを分ける実装を具体的なコードで示します。

2026年7月25日

SSRF(サーバサイドリクエストフォージェリ)とは?起きるパターンと対策法

SSRF(サーバサイドリクエストフォージェリ)の仕組みと起きるパターン、宛先ホストの許可リストとクラウドメタデータ保護(IMDSv2)を軸にした対策を整理します。Railsで URI.open にユーザー指定URLをそのまま渡してしまう危険例と、許可リスト+内部IP検証を組み込んだ安全な実装、DNSリバインディングまで含めた検証の勘所を具体的なコードで示します。

2026年7月24日
この記事をシェア