注記:Aikidoのシークレットスキャン責任者であるZach Riceは、Gitleaksの創設者でもあります。この投稿は、彼のブログに最初に掲載されたもので、シークレットスキャン、ソフトウェアエンジニアリング、オープンソースのトピックを扱っています。
「Regex is (almost) All You Need」では、正規表現パターン、エントロピー、ルールベースのフィルターを組み合わせて使用することが、候補となるシークレットを検出する効果的な方法であることを学びました。正規表現は、候補を特定するために広範囲にわたる検索を行うために使用されます。エントロピーは、捕捉された候補に対する主要なフィルターとして使用され、一般的に使用される英単語の存在や、go.sumのような既知の「安全な」ファイルに対するフィルタリングなどの追加フィルターは最後に適用されます。
エントロピーは誤検知のフィルタリングにはそれなりに役立ちますが、特に一般的なシークレットを評価する際には、まだ多くの改善の余地があります。正規表現による捕捉後の主要なフィルターとして、エントロピーよりも優れたものがあるでしょうか?この投稿では、バイトペアエンコーディングがシークレットスキャンにおいてエントロピーよりも効果的な代替手段となり得るかどうかを検証します。

エントロピー
エントロピーとは一体何でしょうか?John von NeumannがClaude Shannonと話した際に述べたところによると、「誰も本当には知らない」とのことですが、Wikipediaには記載されています。シャノンエントロピーは、文字列の平均的な予測不可能性、つまり各文字がどれだけの情報量を持つかを測定します。文字が均一に分布している場合(多くの異なる文字があり、明確なパターンがない場合)、それぞれの文字は予測が難しく、エントロピーは高くなります。少数の文字が支配的である場合、次の文字は推測しやすく、エントロピーは低くなります。実際には、次のようなものが aaaaaa111111 低いスコアになり、一方で次のようなものは xA9fP2qL0sRw 高いスコアになります。シークレット検出に関して言えば、これによりエントロピーは「ランダムに見える」文字列(候補となるシークレット)を特定するための適切な最初のパスとなります。
しかし、シークレット検出の主要なフィルターとして、本当にランダム性を使いたいのでしょうか?ここで「XではなくYだ」というLLMの決まり文句を許してください。しかし、シークレットは単にランダムなだけでなく、人間が書いたテキストの自然な分布と比較して統計的に異常です。より平たく言えば、シークレットは稀なものです。b64エンコードされた文字列、UUID、実際のシークレット、そして奇妙に見える依存関係の文字列は、現実世界での出現頻度が根本的に異なるにもかかわらず、類似したエントロピースコアを持つことがあります。エントロピーは、「これはランダムに見える」と「これは英語のテキストやソースコードにはほとんど現れない」という違いを区別できません。エントロピーでランダム性を測定する代わりに、文字列がどれほど語彙外であるか、またはどれほど非自然言語的であるかを測定しようとしたらどうでしょうか。
バイトペアエンコーディング
では、文字列がどれほど非自然言語的であるか、または稀であるかをどのように検出するのでしょうか?もちろんバイトペアエンコーディング(BPE)です!バイトペアエンコーディングのトークン化は、学習に使用されたテキストの頻度分布を暗黙的に反映します。一般的な単語やサブワードは長いトークンに結合され、稀な文字列や不自然な文字列は多くの短いトークンに分割されます。
cl100k_baseトークナイザー1を使用したいくつかの例を以下に示します。
- 「Hello World」 → [15339, 1917]
- 「lookingatcomputer」 → [20986, 266, 44211]
- 「kj2h3f2fuaafewa」 → [93797, 17, 71, 18, 69, 17, 69, 4381, 2642, 365, 64]
BPEは、トレーニングデータ内で最も一般的な文字ペアを繰り返しマージすることで語彙を構築するため、そのトークン化は異なるパターンがどれくらいの頻度で出現するかを自然に反映します。私たちが測定しようとしている「稀少性」のようなものに聞こえませんか?
一般的な英単語は、トレーニングで頻繁に出現するため、それぞれ個別のトークンになります。例えば、「password」はトークン[3918]です。「github」はトークン[5316]です。「function」はトークン[1723]です。しかし、`ghp_xK7mP9qL2wR5nT3vJ8fY`のようなランダムなAPIキーはどうでしょうか?

トークナイザーはトレーニング中にその特定のシーケンスを見たことがないため、文字列をより小さなペアに分割し、最終的には個々のバイトに戻り、結果として[876, 79, 3292, 42, 22, 76, 47, 24, 80, 43, 17, 86, 49, 20, 77, 51, 18, 85, 41, 23, 69, 56]としてトークン化されます。これは24文字の文字列に対して22個のトークンであり、トークナイザーがその中にほとんど何も認識しなかったことを意味します。
異なる文字列がどのようにトークン化されるかについては、https://tiktokenizer.vercel.app/?model=cl100k_base をご確認ください。
トークン効率
BPEトークナイザーが稀な文字列を多数の短いトークンに分割する場合、元の文字列長と生成されたトークン数を比較することで、文字列の稀少性を測定できます。これを トークン効率.token_efficiency = len(string) / len(tokens)
自然言語はトークナイザーの語彙によく適合するため、一般的なフレーズは生成されるトークンが少なくなります。シークレットのような文字列はそうではないため、多くのトークンを生成します。
例として、 ghp_xK7mP9qL2wR5nT3vJ8fY。これはトークン効率が1.1です(24文字の文字列が22個のトークンを生成)。のようなフレーズは Hello World 効率が3.7です(11文字が3個のトークンに分割)。もしシークレットが常に低いトークン効率スコアを生成し、日常的なテキストが高いスコアを生成するならば、トークン効率はシークレット検出のための有用な正規表現後フィルターとなり得ます。
このアイデアを検証するために、実際のレポジトリから抽出された、真のシークレットと非シークレットのラベル付きサンプルを何千も含むCredDataデータセットを参照できます。もしトークン効率が実際に稀少性や「非日常言語性」を追跡するならば、CredDataデータセットのシークレット値におけるトークン効率の分布を調べることで、シークレットと非シークレットの間にギャップが明らかになるかもしれません。
CredData
CredDataデータセットは、インデックスファイルとデータファイルに分割されています。インデックスファイルには、ラベル、行と列の範囲、ファイル名などの必要なメタデータが格納されています。これらには実際のシークレット値は含まれていないため、指定された範囲でソースファイルをスライスして、各シークレットを再構築する必要があります。これが私が採用したアプローチです。私は、ラベル付けされたすべてのシークレット値をデータセットから直接抽出しました。これは、トークン効率が単独でシークレットを検出できるかどうかを評価しているわけではないことを意味します。代わりに、トークン効率がすでにキャプチャされた候補シークレットを分類できるかどうかを評価しています。これにより、トークン効率はスタンドアロンの検出器ではなく、正規表現後のフィルタリングステップとなります。
これらのチャートを生成するコードはこちらで確認できます。

これは有望に見えます!トークン効率の良好な最小カットオフ値は2.5のようです。Gitleaksは、一般的なシークレットに対してエントロピーのカットオフ値として3.5を使用しています。
これらのカットオフ値を使用して、分類を見てみましょう。

トークンの効率:精度=57.3% 再現率=98.6% F1=0.725
エントロピー: 精度=21.1% 再現率=70.4% F1=0.32598.6%の再現率は非常に優れています。ほぼすべての真のシークレットを正しく分類しており、残る偽陰性はわずか149件です。トークン効率にはかなりの数の偽陽性がありますが、エントロピーとの差は歴然としています。エントロピーは28,000件のFP(トークン効率の約4倍)と3,000件のFNを生成します。シンプルなワードフィルターを導入することで、両方の手法が改善されますが、F1スコアでは依然としてトークン効率が優位です。このワードフィルターは、4文字以上の単語が複数回出現するシークレットを無視します。

TE + 単語フィルター: 精度=80.4% 再現率=95.8% F1=0.874
エントロピー+単語フィルタ: 精度=76.6% 再現率=67.1% F1=0.715このフィルターは特にエントロピーに対して多くの負担を軽減しますが、トークン効率のFPをフィルタリングするのにも役立ちます。トークン効率では、FPが7894件から2508件に減少し、このワードフィルターを適用した際に導入された新しいFNはわずか308件でした。これにより、F1スコアが大幅に向上します。
これらの結果を再現したい場合は、私のGithubでコードの一部を確認できます。
例
エントロピーが見逃すものの、トークン効率が検出するシークレットをいくつか見てみましょう。
e2aa9ae57d893a1
この文字列のエントロピーは3.125です。かなり高いですが、Gitleaksや他のシークレット検出器がカットオフとして使用する3.5には達していません。e2aa9ae57d893a1は、cl100k_baseトークンとして[68, 17, 5418, 24, 6043, 3226, 67, 26088, 64, 717]を生成し、トークン効率は1.6となり、トークン効率のカットオフである2.5を大幅に下回っています。
mcjrx4
これはパスワードですが、あまり良いものではありません。パスワードは、多くの場合(残念ながら)短く、短い文字列は通常エントロピー値が低いため、エントロピーフィルターにとって難しいカテゴリです。このパスワードのエントロピーはわずか2.58です。しかし、トークナイザーはこれをほぼバイトレベルのトークン[13183, 73, 12940, 19]に分解し、トークン効率は1.5となります。6文字で4トークンです。トークナイザーはこれを自然言語として認識せず、それこそが私たちが求めるシグナルです。
U@kkf8fo!!
別のパスワードです。これは特殊文字があるため興味深いものです。特に一般的なシークレットとパスワードのシークレット検出における課題の1つは、捕捉する正規表現を作成することです ほとんどの シークレットを。シークレットを捕捉することを目的とした正規表現を使用する際の問題は、 ほとんどの シークレットが、メールやURLなどの多くの誤検知を許してしまう可能性があることです。そのため、例えば @ または ! または / のようにキャプチャグループの文字クラスで定義する特殊文字ごとに、より多くの誤検知を許してしまう可能性が高まります。このため、Gitleaksの一般的なキャプチャグループはかなり厳格であることがわかります。 [\w.=-]{10,150}。トークン効率フィルターを使用すれば、そのパターンを緩めてより多くの特殊文字を含めることができる可能性があります。この文脈を踏まえて、この例におけるエントロピーとトークン効率の比較を見てみましょう。 U@kkf8fo!! のエントロピーは2.72で、トークン効率1.42(10文字、7トークン)で、これらのトークン[52, 31, 19747, 69, 23, 831, 51447]を生成します。
パスワードに関する簡単な注意点です。トークン効率は、「password123」や「chibearsfan123」のような悪いパスワードの分類にはあまり適していません。これらのパスワードは基本的に自然言語であり、高いトークン効率値を示します。パスフレーズも、通常は単なる単語であるため、同様にうまくいきません。
パフォーマンス
パフォーマンスへの影響はごくわずかです。キャプチャされたCredDataシークレットのエントロピー計算にかかる文字列あたりの平均時間は4.55 µsであるのに対し、トークン効率2の計算には11.75 µsかかります(cl100k_baseを使用)。2.5倍の違いは大きく見えるかもしれませんが、シークレット検出においては、ボトルネックは正規表現であり、その後に続くエントロピーやトークン効率のようなクイックフィルターではないことを覚えておく必要があります。
Betterleaksによるトークン効率
CredDataデータセットのメンテナーは、正規表現、エントロピー、そしてRNNを使用してシークレットを検出する、CredSweeperと呼ばれる優れたシークレットスキャナーを作成しました。「LLMは誤検知ゼロでシークレットを検出できる」という言説(学術界と産業界3の両方で)が溢れる世界において、Samsungのエンジニアがより「伝統的な機械学習」に基づいたシークレット検出器を構築しているのを見るのは新鮮です。称賛に値します。CredSweeperは、CredDataに対してテストされた際に、0.85という印象的なF1スコアを誇ります。これはかなり良い結果です!新しいToken EfficiencyフィルターをBetterleaksで使って、これを上回れるか見てみましょう。
そうそう。Betterleaksとは何でしょうか?これはGitleaksの遺産の上に構築された新しいプロジェクトです。これについては別の投稿で詳しく説明しますが、知っておくべきことは、私がメンテナンスしているGitleaksのドロップイン代替品であり、その名前の通り、より良くなるということです。
この設定は、いくつかの新しいルールを追加し、既存のデフォルト設定のいくつかの小さな点を調整します。この設定を使用し、CredDataデータセットに対してBetterleaksを実行すると、F1スコアは0.892になります。
(トークンの効率性 + 汎用ルールにおける(低い)エントロピー + ルールの微調整)ベンチマーク結果:
========================================
TP(真陽性): 10796
FP(偽陽性): 1031
TN(真陰性): 42572
FN(偽陰性): 1578
----------------------------------------
精度: 0.9534
精度: 0.9128
再現率: 0.8725
F1スコア: 0.8922かなり良いです。

トークン効率のみを使用した場合の結果は以下の通りです。
(トークン効率の最適化とルールの微調整のみ)ベンチマーク結果:
========================================
TP(真陽性): 10843
FP(誤検知): 1722
TN(真陰性): 41881
FN(偽陰性): 1531
----------------------------------------
精度: 0.9419
精度: 0.8630
再現率: 0.8763
F1スコア: 0.8696トークン効率フィルターを使用する際に、汎用ルールで低エントロピーのカットオフを使用しない場合、約700の偽陽性(FP)が発生します。それでも、汎用ルールにエントロピーフィルターがない場合でも、F1スコアは0.86となり、悪くはありません。
トークン効率フィルターを使用せず、ルール調整とエントロピーのみに頼った場合、どのようなスコアになるでしょうか?
(エントロピーとルールの微調整のみ)ベンチマーク結果:
========================================
TP(真陽性): 8498
FP(偽陽性): 1041
TN(真陰性): 42562
FN(偽陰性): 3876
----------------------------------------
精度: 0.9122
精度: 0.8909
再現率: 0.6868
F1スコア: 0.7756なるほど、0.892対0.776はかなり大きな差です。エントロピーフィルターのみを使用すると、Token Efficiencyフィルターと比較して、2000以上のFNと80のFPが追加されます。
Token Efficiencyフィルターのコードは、BetterleaksのGitHubで確認できます。
func (d *Detector) failsTokenEfficiencyFilter(secret string) bool {
analyzed := secret
if len(analyzed) < 20 && strings.ContainsAny(analyzed, "\n\r") {
analyzed = newlineReplacer.Replace(analyzed)
}
tokens := d.tokenizer.Encode(analyzed, nil, nil)
matches := words.HasMatchInList(analyzed, 5)
if len(matches) > 0 {
return true
}
threshold := 2.5
if len(analyzed) < 12 {
threshold = 2.1
matches := words.HasMatchInList(analyzed, 3)
if len(matches) == 0 {
threshold = 2.5
}
}
return float64(len(analyzed))/float64(len(tokens)) >= threshold
}このフィルターは、チャート比較で使用されているものと比較して、わずかに調整されています。これは、短いパスワードや改行を含むシークレット(候補に対してToken Efficiency分析を実行する前に改行を削除します)を考慮するためです。
その他の注意事項:
- CredDataが提供するベンチマークスクリプトを動作させることができなかったため、私(Claude)は独自のスクリプトを作成しました。これが不適切な科学である場合は申し訳ありませんが、オープンソースなので私の(Claudeの)作業を確認していただけます。
- CredDataデータセットから重複が削除されました。
- 既存のルールに対するすべての新しいルールと調整は、「シークレット固有」ではありませんでした。つまり、ベンチマークを不正に操作しないように努めました。
- CredDataデータセットでFPとしてラベル付けされた一部のシークレットは誤ってラベル付けされているように見えるため、F1スコアは正直なところ±0.05程度かもしれません。
1 すべての例では、cl100k_baseトークナイザーを使用します。
2 各候補シークレットのエントロピーとトークン効率を計算するチャートジェネレータースクリプトから取得したタイミング。
謝辞:GitleaksリポジトリのIssueでこのアイデアを提出してくださったGitHubユーザー「DmitriyAlergant」氏に感謝いたします。この深みに足を踏み入れるきっかけを与えてくれたCredData/CredSweeperのメンテナーの皆様に心から感謝申し上げます。

