
ファイル共有における認証情報・機微データ露出のトリアージ。
開かれた共有が見つかったとき、問題は「このリポジトリに漏洩したキーがあるか」ではありません。「何が露出したのか、そして営業終了までに何をローテーションしなければならないのか」 です。sift はその問いに応えるために作られています: 高い再現率、高速なレビューキュー、そして目視で見つけたものが残り200件のコピーを見つけるルールになるフィードバックループです。

Python 3.11+、標準ライブラリのみ。pip install 不要、インターネット不要、ビルドステップ不要。ロックダウンされた IR ラップトップ上で動作し、まさにそこで必要とされます。```bash sift survey \fileserver\openshare # how big is this thing sift copy \fileserver\openshare C:\IR\case-4471 # take a throttled copy sift scan C:\IR\case-4471 # scan it, opens the triage UI
その場でのスキャンも同様に機能します。まず、偽の認証情報が入った共有に対して試してみてください:```bash
sift demo C:\temp\demoshare
sift.cmd は、任意のディレクトリから動作するランチャーです。フルパスの代わりに sift と入力するには、
C:\Dev\sift を PATH に追加します:```bash
setx PATH "%PATH%;C:\Dev\sift"
Pythonが一切入っていないマシンの場合、`python build_portable.py` は
`dist/sift-secrets-<version>-portable-win64.zip` をビルドします。これは、公式のpython.org
埋め込み可能ランタイムとこのソースツリーを同梱し、同梱の
`sift.cmd`で解凍して実行するだけです。約11 MB、インストール不要、管理者権限不要、コンパイルも
再パッケージングも一切行いません。それがロックダウンされたIR用ラップトップの
フリーズした.exeより優れている理由については、`build_portable.py`のdocstringを参照してください。
---
## なぜgitleaksやtrufflehogだけではだめなのか
どちらも優れたツールですが、解決する問題は異なります。
これらはCI向けに作られた**精度重視**のツールで、誤検知が開発者の午後を
潰しかねないため、主に既知の
ベンダーAPIキーの形をしたものにのみ反応します。trufflehogはさらに進んで、
ベンダーのAPIを呼び出して*検証できる*シークレットを優先します。これは
正規表現では再現できない、本当に優れたシグナルです。
共有ドライブのトリアージは経済性を逆転させます。人間はすでにすべてのヒットを読んでいるため、
誤検知にかかるコストは3秒です。本当にコストがかかるのは**見逃し**です。
ベンダーAPIキーは共有ドライブで間違いなく漏洩します。ウェブルートのバックアップ、デプロイ
スクリプト、誰かのプロジェクトフォルダを部門ドライブにコピーしたもの、そして
本番のStripeキーが入った`.env`もあるでしょう。これらは捕捉する価値があり、siftは
検出します。しかし、これらはgitleaksとtrufflehogがすでにうまく処理している部分でもあります。
ギャップはそれ以外のすべてであり、ファイル共有上ではその大部分を占めます。
| CIスキャナーが見逃すもの | なぜ素通りするのか |
|---|---|
| SQL接続文字列入りの`web.config` | 既知のキー形式ではなく、検証先のベンダーも存在しない |
| `Map-Drives.ps1`(`net use ... /user:`を含む) | ただのシェルコマンドの後に単語が続くだけ |
| `New Hire Setup Guide.docx` | Officeファイルで、バイナリとして読み取られ、完全にスキップされる |
| `unattend.xml`、GPP `Groups.xml` | 誰も検出器を書いていないWindows展開アーティファクト |
| `confCons.xml`、`.rdg`、WinSCP.ini | 可逆的に保存されたパスワードだが、「シークレットの形式」ではない |
| `passwords.xlsx` | ZIPファイルです。プレーンテキストスキャナーはバイナリと見なしてスキップする |
| `.kdbx`、`.pfx`、`id_rsa` | 不透明なバイト列 - *ファイル名*こそが発見事項 |
| 中に接続文字列を含む`.bak` | バイナリなので、読まれることがない |
siftはこれらをカバーし、独自のベンダーキールールを同梱し、**他のツールの
ルールパックと発見結果をインポート**します - gitleaks TOML、Kingfisher/Titus YAML、および
trufflehog JSON - つまり、ツール間で選択する必要はありません。
最も近い既存ツールは[Snaffler](https://github.com/SnaffCon/Snaffler)です。これは、
ファイル名と分類という半分の領域に優れており、ファイル名ルールの直接の
着想源です。しかし、これにないもの - そして400件のヒットがあると実際の
ボトルネックになるもの - はレビューループです。
---
## ループ
1. **共有をスキャン**します。
2. **キューを処理します。** 各発見には、一致箇所がハイライトされた周囲の行が表示されます。
矢印キーでコンテキストを広げられ、ワンクリックでVS Codeの該当行に
ファイル全体を開くか、Notepadで開けます。
3. **見落としを見つけます。** 必ずあります。プレビューでそれをハイライトし、`r`を押します。
4. **siftはパターンを提案**し、既に読んだすべてのデータに対する一致回数を
リアルタイムで表示します。
5. **保存します。** キャッシュされた再スキャンには約1秒かかり、新しいヒットが
既存のトリアージ判断を変更せずにキューに表示されます。
ステップ5こそ、他のステップを意味あるものにする部分です。各発見は
`(path, rule, line, value-hash)` をキーにしているため、再スキャンでは同じ行が再挿入され、あなたの
ステータス、メモ、担当者がそのまま引き継がれます。これがなければ、毎回同じ
300件のヒットを再レビューし、3回目で諦めることになるでしょう。
## コマンド```bash
# size it up first: file count, total bytes, biggest folders, transfer estimates
sift survey \\fileserver\share
# take a rate-limited local copy (resumable; gentle = 5 MB/s by default)
sift copy \\fileserver\share C:\IR\case-4471 --speed gentle
# scan a share and open the triage UI
sift scan \\fileserver\share
# maximum recall: more noise, but a human is reading anyway
sift scan D:\dfs\dept --tier 3
# re-open the UI over the most recent scan
sift ui
# build a share of fabricated credentials, scan it, open the UI
sift demo C:\temp\demoshare
# inherit other tools' vendor-key rules, then use them in the live rescan loop
sift import-rules gitleaks.toml # gitleaks TOML
sift import-rules path/to/kingfisher/data/rules # a directory of YAML
# pull in what the other scanners found, into the same queue
trufflehog filesystem \\fileserver\share --json > th.json
sift import-findings th.json
# hand off to the incident record (redacted unless you say otherwise)
sift export --fmt pdf --status confirmed --out ir-4471.pdf
sift export --fmt csv --out ir-4471.csv
sift export --fmt pdf --no-redact # plaintext; handle as evidence
sift rules # what is loaded
sift selftest # detection tests against a synthetic share
# ask vendors whether confirmed findings are still live. NETWORK. Opt-in.
sift validate --status confirmed
sift が登場する場所では、C:\Dev\sift ディレクトリから
python -m sift を代わりに使用できます。
検出結果は %LOCALAPPDATA%\sift\ 配下のターゲットごとのフォルダーに保存され、作業ディレクトリには決して保存されません。データベースには平文の認証情報が含まれるため、ホームディレクトリからツールを実行しても、そこにこっそり保存されることはありません。各共有には独自のストアが割り当てられるため、2つの案件がトリアージキューを共有することはありません。引数なしの sift ui は最新のストアを開きます。--data DIR で上書きできます。
カスタムルールは %LOCALAPPDATA%\sift\user-rules.json にあるグローバル設定です。ある案件で作成したパターンは、次に調べる共有でも役立ちます。
どちらも UI のヘッダー(パスボックスの隣)と CLI にあります。
サイズ確認は stat のみを使用するウォークです。何も開かないため、SMB 経由でも低コストで、ファイル数、総バイト数、最大のフォルダー、拡張子ごとの内訳、sift が実際に読み取る量、各速度でのコピーにかかる時間がわかります。未知の DFS ルートにスキャナーを向けて待つと、午後が消えていきます。
ローカルにコピーは、まず共有をローカルフォルダーに取得します。以下の理由から実行する価値があります:
転送はレート制限され、デフォルトは 5 MB/s です。午後2時に本番ファイルサーバーへのリンクを飽和させると、調査が2つ目のインシデントに変わります。パスがアイドル状態だとわかっているときに、制限を解除してください。
| Speed | Rate |
|---|---|
| gentle(デフォルト) | 5 MB/s |
| normal | 25 MB/s |
| fast | 100 MB/s |
| unlimited | リンクが提供する最大速度 |
転送は再開できます。同じサイズと mtime を持つ宛先ファイルはスキップされるため、80% で中断された取得は停止した場所から再開されます。ロックされている、または拒否されたファイルは記録されてスキップされ、実行が中止されることはありません。
PDF、CSV、MD、JSON を UI ヘッダーまたは sift export --fmt から出力できます。
PDF はインシデント記録に渡すためのものです。ターゲットを記載した表紙、重要度とトリアージ状態の集計、最も頻度の高いルール、ファイル間で再利用された値、そして重要度ごとにグループ化された検出結果が含まれます。PDF ライブラリなしで直接生成されるため、pip を見たことがないマシンでも動作します。
エクスポートはデフォルトで秘匿化されます。 値はマスクされ、すべてのページにバナーが付きます。エンドポイントがマスキングを無効にするのは明示的な redact=0 の場合のみで、切り詰められた、または不正なリクエストでは漏えいしません。UI で秘匿化を無効にするには警告ダイアログでの確認が必要で、生成されるファイルのすべてのページには UNREDACTED - CONTAINS PLAINTEXT CREDENTIALS というバナーが付けられます。
検出結果のデータベースには実際の値が保持されます。どのパスワードが漏れたかを知って、何をローテーションするかを判断する必要があるからです。境界は、ツールから出ていくものにあります。
すべてがクリック可能です。結果リスト上部の並べ替えコントロールは、重要度、ファイルパス、ルール、トリアージ状態、または最新順で並べ替えられ、逆順にするボタンもあります。サイドバーのファセットは、重要度、カテゴリ、ルール、再利用された値でフィルタリングします。トリアージ、コンテキスト展開、ファイルを開く、ルール作成はすべてボタンです。
以下のキーボードショートカットは、長いキューの操作を高速化するためのものであり、唯一の操作方法ではありません。
スナップショットボタンは、ハイライトされたスニペットを PNG としてレンダリングし、インシデントチケットに貼り付けられます。コピースニペットは Markdown として同じことを行います。
UI はブラウザーウィンドウ内で平文の認証情報をそのままレンダリングします。そのため:
127.0.0.1 のみにバインドし、--unsafe-bind なしではそれ以外を拒否します。SameSite=Strict クッキーに保持します。Host ヘッダーを検証するため、悪意のあるページは DNS リバインディングできません。/api/context は任意のファイル読み取りにはならず、/api/open は任意のプロセス起動にはなりません。検出結果データベースは設計上、平文の認証情報を含みます。IR アナリストは、どのパスワードが漏れたかを知って、何をローテーションするかを判断する必要があるからです。%LOCALAPPDATA%\sift\<target>\findings.db は証拠として扱ってください。共有自体と同じ取り扱いで、案件終了時に削除してください。インシデント境界の外に出る場合は --redact を使用してください。
そして当然のことですが、sift はアクセスを許可されたシステムに対してのみ実行してください。
sift/rules_builtin.py - コンテンツルール。sift/rules_filename.py - ファイル名ルール。どちらも raw 文字列パターンを持つプレーンな Python であり、読みやすく diff も取れます。ユーザールールは .sift/user-rules.json の JSON にあります。
3 つのティアにより、精度と再現率をトレードオフできます:
cpassword、NTLM ダンプ、LDAP バインドパスワード。形状だけで証明になります。ルールには min_digits、min_lowercase、min_uppercase、min_special も指定でき、ノイズの多い 1 つのルールだけを他のルールに触れずに厳格化できます。また examples には、引き続き一致する必要がある文字列を指定します。
examples が有用な半分です。sift selftest は、各ルールを作成時に想定した文字列に対して、マッチ、抽出、そして抑制フィルターという全体の経路で実行します。最後のステップが重要です。実際に発生するリグレッションは、パターンがマッチしなくなることではなく、他の場所で正当な理由により厳格化されたノイズフィルターが、実際の検出結果を黙って食べてしまうことだからです。
すぐに価値を発揮します。既存のルールに examples を追加すると、実際のギャップが見つかりました。アンダースコアは単語文字のため、汎用代入ルールの先頭の \b は DB_PASSWORD、MYSQL_PASSWORD、REDIS_PASSWORD の内部ではマッチを拒否しました。これらは世の中でもっとも一般的な認証情報変数名の 3 つであり、黙って見逃されていました。その例は明らかに正しく見えるのにマッチしませんでした。まさに examples の役割です。
UI で作成したルールはこの機能を自動的に得られます。選択した行がルールの example として保存されるため、6 か月後に編集するルールは、作成のきっかけとなった対象にマッチしなくなったことを知らせてくれます。
sift import-rules は、gitleaks の .toml、Kingfisher/Titus スタイルの .yml、またはそれらのディレクトリを受け取ります。Kingfisher のパックに対しては 1,082 ルール中 1,073 ルールをインポートし、エントロピーの下限、数字と大文字小文字の要件、examples も一緒に取り込みます。YAML は sift/yamlmini.py が読み取ります。これはこれらのパックが使うサブセット用のリーダーであり、依存関係はなく、アンカーやタグについては理解したふりをせずに例外を発生させます。
インポート時に意図的に 2 つのものが除外されます:
validation: ブロック(ルールごとに URL を指定するもの)。 sift が接続するのは validate.py にハードコードされたホストのみです。エンドポイントを指定できるルールパックは、検出結果の送信先を選択することになります。ルールファイルはデータであり、決定事項ではありません。[[:alnum:]] は POSIX クラスです。Python はこれをリテラル文字の集合として読み取り、問題なくコンパイルして、間違ったものにマッチします。変換は各ルールの examples に対して検証されるため、コンパイルを通過しても意味が変わったパターンは、静かに発火しないまま放置されるのではなく拒否されます。Kingfisher のルールのうち 12 個はこのチェックに失敗し、インポートされません。sift 自身の抑制ルールが特定の example を落とすかどうかは、そのルールを失格にするものではありません。これらのパックには意図的に偽のサンプル(keyXXXXXXXX、...EXAMPLE)が含まれているため、プレースホルダーフィルターは example については正しく、パターンについては何も言っていません。その点を厳格にすると、区別が明確になる前に 121 個の動作するルールが破棄されていました。
汎用シークレットルールはノイズのために使われなくなるため、ノイズフィルターはパターンと同じくらい注意深く調整されています。実際の 6,000 ファイルのツリーで測定したところ、以下の抑制ルールにより、検出結果は 1,088 件から 257 件に減り、テストコーパスでは再現率の低下はありません:
password: process.env.DB_PASS は値ではなく変数です。この 1 つの抑制により、ソースツリー内の汎用ルールのノイズのほとんどが除去されます。def login(user: str, password: str) はシグネチャです。.wrangler、.next、site-packages、node_modules、…)に加え、minify されたバンドルとソースマップはスキップされます。機械生成テキストは機械生成の誤検知しか生み出しません。緩い末尾を持つ https://user:pass@host コンテンツルールは、意図的にありません。明らかなバージョンは、ある開発ツリーで 476 回発火しました。minify された JSON には空白がなく、パターンが 1 つの URL から引用符とカンマを越えて走り、数百文字後の無関係な @ を見つけるまで続いたためです。
python tests/run_all.py
12のスイート: 合成共有に対する検出(「CIスキャナが
すでに検出するもの」「このツールが対象とするギャップ」「静かに保たなければならない
おとり」に分割)、コマンドライン、ルール提案ランキング、調査と
スロットル付き取得(実時間に対するレート制限の測定を含む)、
PDFライター(リーダーと同じように解析し直して、リダクションが
ページコンテンツに届いたことを証明する)、gitleaks/trufflehogインポーター、HTTP API(
すべてのセキュリティガードを含む)、UIの静的解析、および
巨大ファイル用のブロックリーダー(最後のブロック内のシークレットが
ファイル全体での真の行番号を報告することを検証)。
試しに使う共有を取得するには:```bash
sift demo C:\temp\demoshare
そのコーパス内のすべての認証情報は捏造されています。
2段階あります。それぞれのリスクは大きく異なるからです。
チェックサムは無料で常に有効です。 ghp_、npm_、Atlassian の ATATT、
Bitbucket の ATCTT、GitLab のルーティング可能な glpat- トークンはすべて、自身の本体にCRC32を
保持しています。これを再計算すれば、かつてインターネットを必要とした問いにオフラインで答えられます。すなわち、
これは本物のトークンなのか、それとも誰かがREADMEに貼り付けたサンプルなのか、ということです。検出結果はキュー内で
checksum ok または malformed としてタグ付けされます。チェックサムが証明するのは形式であり、
有効性ではありません。整った形式のトークンでも、1年前に失効している可能性があります。
ライブ検証は、実行するまでオフになっています。 sift validate は、認証情報が
まだ機能するかどうかをベンダーに問い合わせます。これは scan のフラグではなく独立したコマンドで、
接続するすべてのエンドポイントを最初に列挙するプロンプトで validate と入力する必要があります。
安全のためのルール:
sift/validate.py にハードコードされています。 いかなるルールも(組み込み、ユーザー作成、
他人のパックからインポートしたものを含む)URLを指定できません。それがなければ、ルールパックを
インポートするだけで、共有上のすべての認証情報を、パック作者が選んだ
アドレスに送信できてしまいます。--redact ストアに対しては実行を拒否します。データベースが
インシデント境界の外に出るために値がマスクされている場合、
平文を送信することは、まさに防ごうとしていることそのものです。validation-log.json に記録されます
検証の試行は、その時点であなたのアドレスに帰属する形で、認証情報所有者の監査ログに記録されます。 これは時に、まさに望んだ結果であり、時に監視している敵対者に気付かれるきっかけとなります。 実行する前に判断してください。確認を求めるのは そのためです。
現在、ライブ検証に対応しているのはGitHub、npm、Slack、Stripeです。trufflehog ははるかに多くの検証に対応しています。
trufflehog を実行して import-findings を使えば、両方の結果を得られます。
import-findings を使えば、検証済みのヒットが上位に表示されます。.kdbx や .pfx は
名前で報告されます。sift はそれらを開こうとはしません。name=value に再結合されます。
これがないと、シークレットのキーとその値が別々の列に配置され、
どのルールもペアを認識できません。.7z、.rar、入れ子のアーカイブはフラグが立てられますが、展開はされません。内部が読み取られるのは
ZIPベースの形式と .eml メッセージのみです。--max-size を超えるファイルはブロック単位で読み取られ、キャッシュされません。 それらはスキャンされますが
(4GBの .bak 内の接続文字列も実際の行番号で見つかります)、テキストキャッシュには収まる分しか
保持されないため、1秒以内のキャッシュ再スキャンでは対象外となり、
新しいルールが大きなファイルに到達するのは次回のフルスキャンです。.doc/.xls/.pdf(2007年以前の形式とPDF)は、本格的なパーサーではなくstringsパスで
処理されるため、これらの再現率はOOXML形式よりも低くなります。Apache-2.0。LICENSE を参照してください。
検出テストコーパス(sift/selftest.py、tests/)には、形式は正しいが
意図的に作られた偽の認証情報が含まれています。これらのパスに対するシークレットスキャンの
アラートは、.github/secret_scanning.yml によって抑制されています。
| Flag | Effect |
|---|
--tier 1|2|3 | 再現率のダイヤル。1 = 高シグナル、2 = デフォルト、3 = 見逃しなし |
--redact | ストアとエクスポートの値をマスクします。DB がインシデント境界の外に出る場合に使用します |
--no-ui | ストアに入力して終了します。スクリプト実行用 |
--no-browser | UI サーバーを起動しますが、ブラウザーは開きません(RDP 経由で有用) |
--include/--exclude GLOB | ウォークを絞り込みます |
--no-archives | docx/xlsx/zip コンテナーを開きません |
--no-strings | バイナリに対して strings パスを実行しません |
--no-large | --max-size を超えるファイルをブロック単位で読み取る代わりにスキップします |
--jobs N | ワーカープロセス数(デフォルト: 自動) |
--max-size MB | これを超えるファイルをスキップします(デフォルト: 25) |
--port N | UI ポート(デフォルト: 8973) |
| Key | Action |
|---|
j / k | 次の / 前の検出結果 |
↑ / ↓ | コンテキストを上 / 下に広げる |
c / f | 確認 / 誤検知としてマーク |
x | 一括トリアージ用に選択を切り替え |
o / n | VS Code でその行を開く / Notepad でファイルを開く |
r | ハイライトされたテキストからルールを作成 |
y | 値をコピー |
/ | 検索 |