はじめに
前回 GitHub に API Key が push されてしまった話を書きました。
オンボーディングプロセスがやや形骸化、特に gitleaks については 「テスト用ダミーデータ」や「ドキュメントのサンプルコード」に対する偽陽性(誤検知) の影響で特に長く続いているレポジトリでの運用の課題が改めて出てきました。
実際には問題のない似たデータのために git commit しようとしたときにブロックされるストレスで git commit --no-verify する対応がはじまってしまうと、実際の認証情報を見逃してしまうことにもつながりかねません。
本記事ではケース別の運用方法について考えてみました。
実行環境
- gitleaks : 8.30.1
対応方法
全体的なまとめを下表にしました。
| 対応方法 | 主な用途 | 特徴(メリット/デメリット) |
|---|---|---|
.gitleaksignore |
過去履歴への対応 | 特定コミットハッシュを狙い撃ちできるが、行数指定のため継続メンテが困難 |
| インラインコメント | 単発の特例 |
# gitleaks:allow で明示的。ただし JSON等で使えない |
独自 .gitleaks.toml
|
高度なカスタマイズ | 高い柔軟性があるが、バージョン追従と管理コストが重い |
| デフォルトルールの活用 | 新規開発の標準化 | 追加設定不要で低コスト。ただしルール( stopwords 等)の把握が必要 |
今回は一旦以下の対応を取ることにしました。
- 過去履歴対応:
.gitleaksignore - これからの対応: デフォルトルールの活用
以降、詳細を説明していきます。
.gitleaksignore :過去履歴への対応
前回の記事でも書いたのですが、 Git で一度利用しているブランチに情報がマージされてしまうと取り除くのは困難です 。
過去にテスト用ダミーデータやドキュメントのサンプルコードで認証情報ではないが、 gitleaks で検知されるパターンが入り込んだ場合、これだけのためにこれまでの履歴を消してレポジトリを作り直したり、git-filter-repo で履歴を書き換える複雑なオペレーションをして対応するかは判断の分れるところかと思います。
「履歴に残るのを許容する」という判断をする場合、 gitleaks では .gitleaksignore ファイルを使って対応することができます。
ただし、この機能は公式の README.md でも
Note: this feature is experimental and is subject to change in the future.
https://github.com/gitleaks/gitleaks#gitleaksignore
と記載があり、 今後、破壊的変更が発生する可能性があるものです 。
今回利用した 8.30.1 の時点では2パターンの対応が可能です。
-
ファイルパス:検知タイプ:ファイル内行数での指定 -
コミットハッシュ:ファイルパス:検知タイプ:ファイル内行数での指定
一番の難点は 対象を(コミットハッシュ+)行数で指定する必要がある ことで、ファイルの変更の度に(コミットハッシュと)行数を指定しないといけない点です。
そのため、現状の仕様だと 過去の履歴まで gitleaks でスキャンする場合に対象外とする という対応に使うのが .gitleaksignore ファイルという扱いだと思います。
インラインコメント:単発の特例
インラインコメントで例外許可設定 ができます。
例えば
MY_SERVICE_API_KEY="abcdef12345678901234567890"
という記述があった場合に、以下のように 同じ行に #gitleaks:allow のコメントを付けると検知対象外になります 。
MY_SERVICE_API_KEY="abcdef12345678901234567890" #gitleaks:allow
この方法ですが、以下のような困りどころがあります。
- インラインコメントが書けないファイルフォーマットには使えない (例: JSON など)
- コメントがついた行の修正があった場合は
gitleaksは検知しないのでレビューでしか確認できない
使いどころに気を付けて使わないとなという機能だと思います。
独自 .gitleaks.toml :高度なカスタマイズ
レポジトリ独自の .gitleaks.toml ファイルを作成し、除外検知パターンを定義して対応する方法 もあります。
ただし、この機能は公式の README.md の Configuration の設定例でも随時設定項目の追加/変更が行われており、 バージョンごとにどこまで対応しているかの確認が必要 な状態です。
例えばごく単純に「 gitleaks での検知対象のうち hoge_ から始まるものは除外する」という設定を作成するとすると以下のような内容になります。
# 1. デフォルトの検知ルール(Generic API Keyなど)をそのまま利用する
[extend]
useDefault = true
# 2. 許可リストの設定:v8.25.0 から導入
[[allowlists]]
description = "hoge_ から始まるダミーデータを許可"
# マッチした機微情報の「値」自体がこの正規表現に合致する場合、検知除外とする
regexes = [
'''(?i)hoge_.*'''
]
このファイルをレポジトリのルートディレクトリに配置して以下で Git にコミットする前に検証します。
# グローバル・環境変数・レポジトリルートから .gitleaks.toml を探す
gitleaks dir . -v
# 明示的に .gitleaks.toml を指定
gitleaks dir . -v -c .gitleaks.toml
検知されていた値が検知対象外になればOKです。
この方法ですが、以下の課題があるように思いました。
- 設定ファイルの変更が激しいのでバージョン整合性を取ったり、追従のコストがある
- 除外設定が想定以上の対象を除外している場合があり、新しいリスクになる
この方法は対象のシステムが自分で認証情報を発行するなど、厳密な対応を取る場合でもないと対応コストが高いかなと思っています。このあたりの想定外パターンを防ぐには fuzzing や生成AIによるパターンチェックなどが必要かもしれません。
デフォルトルールの活用:新規開発の標準化
gitleaks のディフォルト設定ファイルである config/gitleaks.toml で検知除外になるパターンを使う方法もあります。検知パターンと検知除外パターンを見て対応するというハックっぽい対応です。
主に活用することになるのは SaaS サービスのパターンではなく、 generic-api-key のケースだと思います。こちらに関しては大量に定義があるのでここを読んで対応することになります。
例えば stopwords のセクションに example があるので、それを使うようにするなど、プロジェクト内で対応可能な単純なルールに落とし込むのがよいかと思います。
もう少し厳密な対応可能幅を検討して決めたいのであれば、これも生成AIにルールを読ませるのがよいかもしれません。単純に正規表現も複合ルールも複雑なのですが、v8.28.0 からマッチングのパターンも変わっています。
おわりに
検知ツールは検知漏れがあるとまずいのでどうしても厳しめの評価を検知をせざるを得ず、結果偽陽性が出やすい状態になっています。
偽陽性が出るのが常態的になってしまうと、どうしても git commit --no-verify によるコミットや、ツール適応の回避をしたくなる心理が働いて運用の形骸化も起こりえます。
今回 gitleaks での対応方法はそれぞれに課題点を抱えてはいますが、過去履歴とこれからの対応それぞれについて再検討できたのはよかったと思います。
結局のところ、セキュリティは利便性との一部トレードオフが出てきてしまいます。複雑な正規表現を読み解くのは大変ですが、最近では生成AIに config/gitleaks.toml をコンテキストとして与えて
『このルールに合致しつつ、stopwordsで除外されるサンプルを作って』
とプロンプトを討つことで頼むことで、のハードルも劇的に下がっています。
ツールの特性を理解し、形骸化しないセキュリティ運用を目指していきたいと思います。