フロントエンドに「鍵」を置く罪――なぜあなたのAPIキーは、世界中の攻撃者に丸見えなのか
現場でインシデント対応をしていると、決まって同じ光景を目にする。数日間徹夜して作り上げた新機能、そのリリース直後の深夜3時。突然、AWSの請求が跳ね上がり、S3バケットのデータが暗号化され、ランサムウェアの脅迫状が届く。「なぜ? 認証は通しているはずなのに」。
答えはシンプルだ。ブラウザから見えるソースコードの中に、本番環境用のAPIキーやシークレットが転がっていたからだ。
エンジニア諸君、これを読んでいる君のGitHubリポジトリや、今ブラウザで開いている検証環境のソースコードを確認してほしい。そこに const API_KEY = 'sk_live_...' という文字列がハードコーディングされていないか? もしそうなら、君は鍵のかかっていない家の玄関に、わざわざ「合鍵はこちら」という看板を出して放置しているのと同じだ。
1. なぜ「隠したつもり」が通用しないのか(PoCの視点)
攻撃者は、高度なハッキング技術を駆使する以前に、「ブラウザから見えるものは全て公開情報である」という大前提を突いてくる。
例えば、ReactやVueで書かれたフロントエンドコード。これらはビルドされても、ソースマップ(.mapファイル)を適切に管理していない場合や、単に難読化が不十分な場合、ブラウザのデベロッパーツール(F12)で誰でも中身を確認できる。
攻撃者の具体的な動線:
1. 静的解析: 公開されたJSファイルをダウンロードし、正規表現で API_KEY や access_token といったキーワードを抽出する。
2. 自動スキャン: GitHubのコミット履歴を自動巡回するボット(TruffleHogなど)に、君のキーを即座に特定させる。
3. 悪用: 特定したキーを使って、君のクラウドサービス(AWS, GCP, Stripeなど)のAPIを叩き、不正なリソースを生成したり、顧客データベースを窃取する。
「自分だけが知っている」はセキュリティの世界では幻想だ。ブラウザという「敵対的な環境」に機密を置いてはいけない。
—
2. 「バックエンド・プロキシ」という守り方
では、どうすればいいのか? 答えは「クライアントサイドに機密を持たせず、バックエンドを介して通信する」ことだ。これを「バックエンド・プロキシ」と呼ぶ。
フロントエンドは「認証済みユーザーの代理」として自社のAPIサーバーへリクエストを投げ、バックエンドが責任を持って「秘匿された環境変数」から本物のAPIキーを読み込み、サードパーティ(StripeやOpenAIなど)と通信する。
実装サンプル:Node.js (Express) によるプロキシ
クライアントから直接サードパーティを叩くのではなく、自社のサーバーを経由させる。
// server.js (バックエンドの安全な実装)
const express = require(‘express’);
const axios = require(‘axios’);
require(‘dotenv’).config(); // .envファイルから機密情報を読み込む
const app = express();
app.post(‘/api/secure-proxy’, async (req, res) => {
try {
// フロントからは機密情報を受け取らず、サーバー側の環境変数を使用する
const response = await axios.post(‘https://api.third-party.com/v1/data’, {
data: req.body.data
}, {
headers: {
‘Authorization’: Bearer ${process.env.SECRET_API_KEY} // ここが肝!
}
});
res.json(response.data);
} catch (error) {
res.status(500).send(‘内部エラーが発生しました’);
}
});
フロントエンド側は、単に fetch('/api/secure-proxy', ...) を呼ぶだけでいい。APIキーはブラウザに一切露出しない。
—
3. インフラレベルでの防御(Nginxの設定)
万が一、設定ミスで config.js のようなファイルが露出してしまった場合でも、インフラ側で防ぐガードレールを敷くべきだ。NginxなどのWebサーバーで、機密ファイルのアクセスを物理的に遮断する。
nginx.conf の設定例
.envや.gitといった機密関連ファイルへのアクセスを一切拒否する
location ~ /\.(env|git|htaccess|sql) {
deny all;
return 404;
# 攻撃者に「ファイルがある」と悟らせないよう404を返す
}
—
4. 現場のエンジニアへ送る「絶対ルール」
最後に、明日から君のチームで徹底すべき3つの鉄則を記す。
1. .env ファイルを絶対にコミットしない: .gitignore に必ず追加し、環境変数管理サービス(AWS Secrets ManagerやGitHub Secrets)を利用すること。
2. フロントエンドに「認証用シークレット」は置かない: 置いていいのは「公開可能なAPIキー(Public Key)」だけ。それも権限を最小限(読み取り専用など)に絞ること。
3. CI/CDでの自動スキャン: GitHub Actions等で gitleaks などのツールを導入し、リポジトリに機密情報が混入した瞬間にビルドを止める仕組みを作ること。
セキュリティとは、何か特別な魔法を使うことではない。「漏れてもいい場所」と「決して漏らしてはいけない場所」を厳格に切り分ける、泥臭い設計の積み重ねだ。
君たちが書くコードが、誰かの資産を破壊する凶器にならないよう、今日からこの設計思想を標準にしてほしい。現場からは以上だ。また次のインシデントの裏側で会おう。
コメント