ブラウザのJavaScriptバンドルに漏洩したシークレットを検出する方法
問題
シングルページアプリケーションやライブラリをバンドルすると、開発中にハードコードされたAPIキー、トークン、署名鍵などがそのままソースコードに残ることがあります。これらのシークレットは、公開されたバンドルファイルから容易に抽出され、不正利用のリスクとなります。
メカニズム
バンドル内のシークレットを検出する典型的なフローは次の通りです。
-
バンドルの取得
ビルドアーティファクト(例:main.js,chunk.vendors.js)をHTTP経由またはCIアーティファクトから取得します。 -
トークン化とパターンマッチ
ファイルをUTF‑8テキストとして読み込み、正規表現や構文解析で以下のようなパターンをスキャンします。-
AKIA[0-9A-Z]{16}(AWSアクセスキー) -
sk_live_[0-9a-zA-Z]{24}(Stripeライブシークレットキー) -
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9(JWTのヘッダー) -
-----BEGIN PRIVATE KEY-----(PEM形式の秘密鍵)
-
-
コンテキストフィルタ
マッチした文字列がコメントや文字列リテラル内にあるか、実際にコードで使用されているかを判定します。たとえば、"API_KEY": "sk_test_…"のようなオブジェクトリテラルは使用されている可能性が高く、単なるコメント内の例は除外します。 -
エントロピー評価
高いシャノンエントロピー(例: 4.0以上)を持つトークンは、ランダム性が高く本物のシークレットである可能性が高いため、スコアリングに利用します。 -
結果の報告
ファイル名、行番号、マッチした文字列、そして推定リスクレベルを出力します。
例
以下は、Webpackで生成されたバンドルからAWSシークレットキーを検出する簡単なNodeスクリプトです。
const fs = require('fs');
const path = require('path');
const AWS_SECRET_REGEX = /[A-Za-z0-9\/+]{40}/; // 簡略化された例
function scanBundle(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
const findings = [];
lines.forEach((line, idx) => {
const match = line.match(AWS_SECRET_REGEX);
if (match) {
// ここでは単純にマッチを報告。実際はコンテキストフィルタを追加
findings.push({ file: filePath, line: idx + 1, secret: match[0] });
}
});
return findings;
}
// 使用例
const bundleDir = './dist';
const bundleFile = path.join(bundleDir, 'main.js');
const results = scanBundle(bundleFile);
if (results.length > 0) {
console.log('潜在的なシークレットが見つかりました:');
results.forEach(r => console.log(`${r.file}:${r.line} → ${r.secret}`));
} else {
console.log('シークレットは検出されませんでした。');
}
このスクリプトは、バンドルファイルを1行ずつ読み込み、正規表現で疑似的なAWSシークレットキーを抽出します。実際の運用では、キーの前後にある変数名やオブジェクトキーをチェックし、エントロピーを計算して偽陽性を減らします。
注意点
-
ミニファイと難読化
本番バンドルはミニファイされ、変数名が短縮されるため、単純な文字列検出では漏れが生じます。AST(抽象構文木)を使ってリテラル値を取得すると、ミニファイ後でも検出しやすくなります。 -
ソースマップの利用
ソースマップが公開されている場合、元のソースコードに戻してシークレットを探すと精度が向上します。ただし、ソースマップ自体も機密情報を含む可能性があるため、公開しない方が安全です。 -
ライセンスとサードパーティコード
node_modules内のライブラリにもシークレットが含まれていることがあります。自分のコードだけでなく、依存関係のバンドルも対象に含めるか、別途スキャンする必要があります。 -
頻度とトリガー
プルリクエストごと、または夜間バッチでバンドルアーティファクトをスキャンする仕組みをCIに組み込むと、漏洩を早期に検出できます。
Learn more about detecting exposed secrets in client bundles.
Details, and a free check: https://keydrift.dev/scan?utm_source=qiita&utm_campaign=socialhat&utm_medium=social&vsr=a39abf9e46308bbf7e82