GitLab Secret Detection ADR 009: 汎用シークレットの検出
この ADR は、ルールベースのカバレッジの範囲外にある、非構造化シークレットおよび機密性の高い認証情報の種類を検出するためのアプローチを概説します。
コンテキスト
汎用シークレット検出は、高エントロピー文字列を検出するための Vulnerability Research チームによる実験を通じて最初に検討されました。この実験は専門化されたディテクターで構成されており、各ディテクターは与えられた文字列が高エントロピーを持つかどうかを独立して識別するように設計されていました。Secret Detection チームはその後、汎用シークレットを検出するための Entropy engine ワークフローを開発するため、実験からいくつかの概念を借用しました。
スコープ
Entropy engine の主なスコープは、スキャンペイロード内のリテラルのうち、特定のエリア(ホットスポット)での出現位置と適切なコンテキスト(例: 周囲の機密キーワード)に基づいて、構文的に潜在的なシークレットのように見えるものを識別することです。このエンジンは、変数名参照などの構文的に安全な文字列を識別されたリテラルから除外します。リテラルが潜在的なシークレットかどうかを意味論的に評価すること(すなわち、プレースホルダーやテスト値かどうかなどのリテラルの意図の評価)はエンジンのスコープ外です。
ソリューション
ルールベースの正規表現スキャンエンジンとは異なり、汎用シークレットにはペイロードから正確なシークレット値を抽出するための事前定義されたパターンがありません。汎用シークレットが出現しやすい特定のエリア(ホットスポット)から文字列リテラルを抽出し、抽出した内容を分析します。抽出された文字列リテラルは、変数名参照のようなランダムな値に過ぎないこともあります。抽出した値と周囲のコンテキストを使用して複数のテクニックを適用し、ランダムな値を持つリテラルをフィルタリングします。
Entropy engine は3つのステージを経て汎用シークレットを検出します。
- リテラル抽出: シークレットが出現する可能性のある対象ペイロード内のエリアから、可能性のある文字列リテラルをすべて抽出します。
- タイプ評価: 抽出されたリテラルの基礎となるタイプ(例: JWT、Base64Encoded)を決定し、フォーマット検証やデコードなどのタイプ固有の操作を実行します。
- フィルタリング: 抽出後のステップでフィルターを適用し、エンジンの結果を返す前に、リテラルとして抽出されたランダムな値を除去します。
Entropy engine のステージの高レベルの図:

シークレットカテゴリー
汎用シークレット検出ステージの詳細に進む前に、シークレットについて少し理解しておきましょう。
典型的なシークレットはさまざまなプロパティ(エントロピー、大文字と小文字の切り替え比率、辞書の単語など)を示し、任意のリテラルをシークレットとして評価するための共通のルールセットを持つことが困難です。例えば、superhuman@123 は API トークンとしては偽陽性ですが、DB パスワードとしては潜在的な真陽性です。これが、シークレットをカテゴリー分けし、各カテゴリーに適用される特定のルールセットを持つ必要がある理由です。
技術的に、シークレットはそれぞれ特定のプロパティを示す以下の2つの方法のいずれかを使用して生成されます。
機械生成: 高エントロピー。高い大文字と小文字の切り替え比率。小サイズ(<=3文字)から辞書の単語なし。それ以上(>3)の繰り返しシーケンスなし。通常、長い文字列。
人間生成: 主に機械生成文字列と反対で、一般的な辞書の単語の使用などの目に見える人間のシグナルがあります。文字の繰り返し/シーケンス。低い大文字と小文字の切り替え比率。
これらのメソッド固有のプロパティは、リテラルを評価するための特定のルールを定義するのに役立ちます。ただし、シークレットが機械生成か人間生成かを値だけから判断することは常に可能というわけではありません。値そのものを超えて、周囲のコンテキストから推測されるシグナルを見る必要があります。
シークレットの性質に基づいて、以下のシークレットカテゴリーを考案しました。
パスワード: 人間によって定義されたシークレット。人間が定義したパスワードの典型的なシグナルは、“pass”、“password”、または “pwd” キーワードを含む変数名に割り当てられたシークレット、および URI 文字列のパスワードセクションです。
API キー: ベンダー固有のシークレット。典型的なシグナルには、周囲のコンテキストでのベンダー/製品名の使用が含まれます。例:
aws_secret_key = "..."または{"aws": {"secret_key": "..." }}のようなネストされた構造の場合もあります。暗号鍵: これらは複数行のシークレットで、通常は固定のプレフィックス/サフィックスフォーマットを持つ証明書と秘密鍵です。
汎用: これは、シークレットがプロパティを示すものの、周囲のコンテキストから特定のカテゴリーを推定するための有用なシグナルがないシナリオを表すその他のカテゴリーです。例えば、
var SECRET = "2m49dn2-1Z3-rM2-4Q32"です。
リテラル抽出
これは汎用シークレット検出の最初のステージです。スキャンペイロード内の特定のエリア(ホットスポット)から文字列リテラルを抽出します。そこでは、シークレットがカテゴリーのいずれかとして現れる可能性が高いです。一般的なホットスポットには、変数の代入、辞書の代入、URL パラメーター、CLI フラグ、接続 URI、認証ベアラートークン、コメントなどがあります。
リテラル抽出には2つの一般的なアプローチがあります。
正規表現ベースの抽出: 各ホットスポットタイプ専用の正規表現パターン。長所: 最小限の労力、低いエンジニアリングの複雑さ、ほとんどのシナリオをカバー、言語に依存しない。短所: 壊れやすく、特定のシナリオで偽陽性と偽陰性が発生しやすい。
AST ベースの抽出: ペイロードの AST ツリーを解析し、AST クエリを介してリテラルを抽出する。長所: より高い精度、堅牢、低いメンテナンス負担。短所: 言語固有(文法が必要)、ペイロードの言語が判断できない場合は精度が低い、より多くの文法サポートのためのバイナリサイズの増加、大きなペイロードでのリソース消費の増加(ストリームされない限り)。
どちらのアプローチにも明確な強みと弱みがあります。AST ベースのアプローチは、プレーンテキストコンテンツや言語情報が欠けているスキャンペイロードには機能しないため、正規表現ベースのアプローチは必要なフォールバックとなります。したがって、AST ベースのアプローチを採用するかどうかに関わらず、正規表現ベースのアプローチは不可欠です。
まず正規表現ベースのアプローチを使用し、次に人気のある言語のみに AST ベースのアプローチを実装してバイナリサイズを削減し、両方のアプローチの利点を組み合わせることにしました。特定のシークレットカテゴリーに一致するリテラルを抽出するために、各ホットスポットの正規表現パターンを作成します。例えば、パスワードを抽出するために、左辺(LHS)が password、pwd、または pass などのキーワードを含み、右辺(RHS)が文字列リテラルである代入のような式を定義します。
リテラルタイプ評価
抽出ステージがシークレットカテゴリーのいずれかにリテラルを抽出して分類する一方、このステージはシークレットカテゴリーに関わらず、リテラルの基礎となるフォーマット(別名リテラルタイプ、例: JWT、Base64Encoded、Hex)を決定します。このステップは、プレーンテキストに隠れたシークレットを見つけるために必要です。例えば、JWT トークンはデコードされた内容に秘密鍵を発見するまで重要度が低いように見えることがあります。リテラルタイプを識別することで、タイプ固有の処理も可能になります。エントロピーの閾値は JWT と UUID 文字列で異なります。
現在、以下のフォーマットを識別します。
UUIDBase64Base64URLJWTJWEPasetoHexAlphaNumASCIIRaw(未確定; デフォルト)
フィルタリング
ホットスポットから抽出され、key、token、または secret などの高信頼性キーワードに割り当てられたリテラルでも、偽陽性である可能性があります。例えば、値内の変数参照や意図しない一致(var primaryKeyName = "transaction_id" や TOKEN_SOURCE=https://stripe.com/webhook/test など)があります。
リテラル、そのカテゴリー、基礎となるタイプ、および周囲のコンテキストをキャプチャして感度を判断します。値によるフィルタリング(絶対/相対ファイルパス、URL、配列/辞書アクセス、環境変数アクセス)、LHS 変数名キーワード(例: signing、primary、column)、ファイルパスキーワード(例: node_modules/、docs/、vendor/)など、複数のフィルタリング技術を使用します。
このステージでは、シークレットである可能性が低い抽出されたリテラルをフィルタリングし、残りのリテラルを Entropy engine の最終スキャン結果として返します。
エンコードされたリテラル内の汎用シークレット
リテラルタイプを識別することで、Base64、URLエンコード、JWT フォーマットなどのデコードをサポートするエンコードされたリテラル内でシークレットを検索する機会が開かれます。これらのリテラルをデコードし、デコードされたコンテンツ内のホットスポットを一度だけ再帰的に検出ワークフローを実行してチェックします。
将来のスコープ
AST ベースのリテラル抽出
リテラル抽出セクションで述べたように、既存の正規表現ベースのアプローチを補完するために AST ベースの抽出を実装する予定です。これにより、サポートされている言語の精度が高まり、正規表現ベースの抽出をフォールバックとして維持します。
ML 統合
将来のイテレーションでは、検出精度を高めるために機械学習モデルの統合を探求します。
- コンテキスト対応の分類: ML モデルを使用して、変数の代入と文字列使用パターンの背後にある意図をより深く理解する
- 偽陽性の削減: 過去の検出データでモデルをトレーニングして、偽陽性につながるパターンを特定する
- 適応型閾値: ファイルのコンテキスト、言語、過去のパターンに基づいてエントロピーの閾値を動的に調整する
より多くのエンコード検出のサポート
さまざまなエンコード形式に隠されたシークレットの検出サポートを拡張します。
- カスタムベースエンコーディング: Base32、Base85、および他のエンコードスキームのサポート
- ネストされたエンコーディング: 複数回エンコードされたシークレットを検出する(例: URL エンコードされた文字列の Base64)
- 圧縮検出: 圧縮されたデータフォーマット内のシークレットを識別する
