対策・学習22分で読める

gitleaksでコミット前に秘密を止める:APIキー漏洩をpushの前で食い止める

APIキーや秘密鍵をコミットしてpushすると、消しても履歴に残り失効が必要になります。gitleaksをpre-commitフックとCIで組み合わせて秘密を検出する方法と、検出後の失効までを解説します。

本文を読む
公開日
更新日
目次

対象:個人開発・小規模運営でGitを使っていて、「APIキーをうっかりコミットしないか不安」という人。攻撃側の手口は扱わず、自分のリポジトリから秘密の流出を止める仕組みだけを扱います。

当サイトの視点:.gitignore だけでは秘密は守れない

「.envを.gitignoreに入れているから大丈夫」とよく言われますが、これには抜けがあります。.gitignoreは新しく追跡対象に入れないだけで、既にコミット済みの秘密や、急いでgit add -fで強制追加した秘密、.env.local.bakのような想定外のファイル名は捕まえられません。gitignoreが防げるのは事故の一部だけです。実際にコミット内容を見て「秘密らしい文字列」を検出する仕組み、つまり検出器が別に要ります。それがgitleaksの役割です。

なぜ「コミット前」で止めるのか

秘密が危険なのは、Gitが履歴を永久に保持するからです。最新のファイルから消しても、過去のコミットには残る。そしてpushされた瞬間、その履歴は他者の手元やサーバ、CIのログにも複製されます。

履歴に残る
最新から消しても過去コミットに残存
push=拡散
他端末・サーバ・CIログに複製される
失効が必須
一度漏れた鍵は再発行しかない
2段階の検査
pre-commit と CI で二重に止める

つまり秘密対策は、漏れた後の回収ではなく漏れる前の阻止が最も効きます。これは「公開ディレクトリに秘密を置き忘れない」棚卸し(→ webrootの棚卸し)と同じ発想を、コードに当てはめるものです。

どの段階で検出するか

秘密がコードから世界に出ていく経路には、2つの検査を置けます。手元のコミット時と、共有される直前(CI)です。

コード内の秘密

APIキー・鍵・トークン

→

検査1:pre-commit

コミット時にローカルで検出・中断

→

検査2:CI / cron

push後にマージ/デプロイ前で検出

→

公開・共有

ここまで来たら漏れた前提

秘密の流出経路と2段階の検査。pre-commitはローカルで、CIはマージ/デプロイ前に止める。

gitleaksの組み込み方

1

まず既存の履歴をスキャンする

導入時に一度、リポジトリ全体の履歴を gitleaks git でスキャンする(v8.19.0より前の書き方では gitleaks detect)。過去のコミットに残っている秘密を洗い出すのが最初の仕事。ここで見つかったものは、後述のとおり失効が前提。
2

pre-commitフックで1段目の検査を入れる

コミットのたびに gitleaks git --pre-commit --staged(旧 gitleaks protect --staged)を走らせ、秘密を含むコミットをその場で中断する。pre-commitフレームワーク(.pre-commit-config.yaml)に追加すると、チームでもローカルでも同じ検査が掛かる。
3

CI / cron で2段目の検査を入れる

フックは各自が無効化できるため、共有される手前でもう一度検査する。CIのジョブ、あるいは自前運用なら定期的なスキャンとして gitleaks git を回し、すり抜けを捕まえる(→ 依存監査と同じく機械監視の発想)。
4

誤検出は .gitleaks.toml で調整

テスト用のダミー鍵やサンプル値が引っかかったら、ルールやallowlistを .gitleaks.toml に書いて調整する。「警告を消す」のではなく「なぜ安全かを記録する」のが正しい運用。根拠のない除外は次の事故の原因になる。

検出したら『失効』が先、履歴の書き換えは後

一度コミット・pushされた秘密を見つけたら、最優先はその鍵・トークンの失効と再発行(ローテート)です。git filter-repo 等で履歴から消すのは大事ですが、それは『これ以上広げない』ための後処理にすぎません。公開リポジトリ・フォーク・CIログ・他人のクローンに渡った秘密は回収不能と考え、まず無効化してから履歴をクリーンにしてください。順番を逆にすると、消している間に使われます。

.gitignore 頼み

  • 新規の追跡しか防げない
  • 既にコミット済みの秘密は検出できない
  • git add -f や想定外のファイル名に無力
  • 「入れたつもりがない」を検証できない

gitleaks(検出+失効運用)

  • 作業ツリーと履歴の中身を実際に検査
  • pre-commitとCIの二段で止める
  • 検出したら失効までを手順化
  • 誤検出は根拠を記録して除外

コマンドの使い分け(v8.19.0以降)

gitleaksはv8.19.0でコマンドが整理されました。古い detect と protect は今も動きますが非推奨で、ヘルプには表示されません。新しく組むなら次の3つを使います。

コマンド調べる対象主な使いどころ
gitleaks gitGitのコミット履歴導入時の全履歴スキャン、CI、定期スキャン
gitleaks git --pre-commit --stagedこれからコミットする変更(ステージ済みの分)pre-commitフック(公式フックの中身もこの形)
gitleaks dirフォルダやファイル(Gitで管理していないものも可)公開ディレクトリ、バックアップ、配布用のzipを展開したフォルダ
gitleaks stdin標準入力で渡した文字列他のコマンドの出力やログを流し込んで調べる

検出が1件以上あると終了コード1を返し、何もなければ0を返します。CIやcronでは、この終了コードで失敗扱いにできます。出力には --redact を付け、見つかった秘密そのものがログに残らないようにします。

設定例:pre-commitフックとGitHub Actions

pre-commitフレームワークを使う場合は、リポジトリの直下に次のファイルを置きます。rev は使う時点の最新リリースに合わせます(2026年10月時点の最新はv8.30.1)。

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks

id は、Goで組み立てる gitleaks のほかに、Dockerで動かす gitleaks-docker、PCに入れ済みのgitleaksを使う gitleaks-system も選べます。ファイルを置いたら、各自の手元で次を実行します。

pip install pre-commit        # pre-commit本体を入れる
pre-commit install            # このリポジトリにフックを登録する(クローンごとに1回)
pre-commit run --all-files    # 登録直後に、全ファイルを一度検査する
pre-commit autoupdate         # 時々実行して、rev を新しいリリースに上げる

GitHubでCIを回すなら、公式のGitHub Actionが使えます。履歴全体を調べるため、チェックアウトでは fetch-depth: 0 を指定します。

# .github/workflows/gitleaks.yml
name: gitleaks
on:
  pull_request:
  push:
  schedule:
    - cron: "0 4 * * *"
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

組織アカウントのリポジトリでは、このActionにライセンスキー(GITLEAKS_LICENSE)が必要です。公式の説明では、gitleaks.ioで申し込めば無料で発行され、個人アカウントのリポジトリでは不要とされています。自前のGitサーバーやGitHub以外のCIなら、ジョブの中で gitleaks git --redact を直接実行すれば同じことができます。

どこで検査するか:4つの場所の比較

1か所だけでは抜け道が残ります。それぞれが何を止め、どこに穴があるかを並べると、組み合わせる理由が分かります。

検査の場所止められる時点調べる範囲抜け道・限界
pre-commitフック(手元)コミットする前ステージした変更git commit --no-verify や SKIP=gitleaks で飛ばせる。クローンしただけではフックは入らない
CI(GitHub Actionsなど)マージ・デプロイの前pushされたコミットと履歴push自体は済んでいる。公開リポジトリなら、その時点で漏れた前提
定期スキャン(cron)後から見つけるリポジトリ全体の履歴発見までに時間差がある。ルール更新で新しく見つかる分を拾える
GitHubのプッシュ保護GitHubへのpush時GitHubが対応する種類の秘密理由を選べば回避できる。GitHub以外のサーバーには効かない

GitHubのプッシュ保護は、個人ユーザー向けには公開リポジトリへのpushで既定で有効になっています。無料で効く安全網ですが、手元のコミットは止めず、対応していない形式の鍵や自作の接続文字列は通ることがあります(詳しくは GitHubに公開された認証情報が今も有効な理由)。gitleaksは、その手前と、GitHubの外を受け持ちます。

自分の環境で確認する方法

導入済みのつもりでも、実際に動いているとは限りません。自分のリポジトリで、次を順に確かめます。

1

gitleaksのバージョンを確かめる

gitleaks version を実行します。v8.19.0より古いと gitleaks git などの新しいコマンドが使えないので、更新します。

2

フックが登録されているか確かめる

.git/hooks/pre-commit があるかを見ます。git config --get core.hooksPath で別のフォルダが設定されている場合は、そちらを見ます。Gitの仕様上、フックはクローンで複製されないので、新しいPCや新しいクローンでは pre-commit install を実行し直します。

3

履歴全体を一度スキャンする

リポジトリの直下で gitleaks git --redact -v を実行します。検出が0件なら終了コードは0です。見つかった場合は、出力されたファイル名とコミットをもとに、その鍵がまだ有効かを発行元の管理画面で確かめます。

4

Gitの外にあるフォルダも調べる

デプロイ先の公開フォルダ、バックアップ、配布用に作ったzipの中身は、Gitの履歴スキャンでは見えません。gitleaks dir (フォルダのパス) --redact で調べます(→ 公開ディレクトリの棚卸し)。

5

.envが一度でもコミットされていないか確かめる

git log --all --oneline -- .env で、過去に .env を含むコミットがあったかを確認します。1行でも出たら、その時点の中身は履歴に残っています。

誤検出の扱い方

テスト用のダミー鍵やドキュメントの例が検出されることはよくあります。除外の方法は3つあり、範囲の狭い順に使います。

  • 行単位:その行に gitleaks:allow というコメントを書く
  • 検出結果単位:出力に表示される識別子(フィンガープリント)を、リポジトリ直下の .gitleaksignore に書く(公式には実験的な機能とされています)
  • ファイルやパターン単位:.gitleaks.toml の許可リストに書く

.gitleaks.toml を自分で作るときは、[extend] に useDefault = true を書いて既定のルールを引き継ぎます。これを書かずに自作のルールだけを置くと、既定のルールで拾えていた鍵が検出されなくなります。

古いリポジトリで既存の検出が多い場合は、--baseline-path に過去のレポートを渡すと、新しく増えた分だけを報告させられます。ただし、基準にする前に、そこに含まれる鍵は失効させておきます。基準に入れることは、鍵を安全にすることではないからです。

よくある間違いと直し方

  • フックを設定ファイルに書いただけで安心する:pre-commit install を実行しないと動きません。各自が実行し、CIでも同じ検査を回して二重にします。
  • --no-verify で検査を飛ばすのが習慣になる:飛ばしても、CIの検査をマージの必須条件にしておけば止まります。
  • CIのログに秘密をそのまま出す:--redact を付けます。CIのログも、閲覧できる人が多い場所です。
  • CIで浅いクローンのまま検査する:最新のコミットしか見ないため、履歴の秘密を見落とします。fetch-depth: 0 を指定します。
  • 履歴から消して終わりにする:GitHubの公式手順も、まず失効・差し替えを最初の手順にしています。フォークやクローン、キャッシュに残った分は消せません。
  • 本物の鍵を「誤検出」として許可リストに入れる:除外の理由をコメントに書き、レビューで確認します。

実際の事例:公開されたままのアクセスキー

トヨタ自動車は2022年10月7日、コネクティッドサービス「T-Connect」の利用者向けサイトについて、ソースコードの一部がGitHub上で公開状態になっていたと発表しました。同社の発表によると、経緯は次のとおりです。

  • 2017年12月、サイトの開発委託先が、ソースコードの一部を誤って公開設定のGitHubアカウントにアップロードした
  • そのソースコードに、顧客情報を保存するデータサーバーへのアクセスキーが含まれていた
  • 2022年9月15日に非公開にし、9月17日にアクセスキーを変更した
  • 対象はメールアドレスと顧客管理番号の296,019件。アクセス履歴から第三者の利用は確認できなかったが、完全には否定できないとしている

公開から非公開までは約5年です。この事例は、自社の管理するリポジトリの外、つまり委託先のアカウントで起きました。自分のリポジトリにgitleaksを入れるだけでは届かない場所があるので、キーそのものの権限を絞り、定期的に差し替える運用も合わせて必要です。また、同社も公開を止めた2日後にキーを変更しています。見つけたらキーを替える、という順番はこの事例でも同じです。

非公開のリポジトリでも安心はできません。2026年のCAMPFIREの事案では、同社の発表によれば、不正利用されたGitHubの認証情報を起点に、GitHub上で閲覧できた情報からクラウドの認証情報が取得されました(→ CAMPFIREの不正アクセス)。非公開のリポジトリにも秘密を置かず、gitleaksで定期的に確かめておく理由になります。

当サイトはどうしているか

当サイトは、そもそも秘密をGitに一切入れない設計を基本にしています。接続情報やAPIキーはサーバ上の保護された設定(権限を絞った環境ファイル)にだけ置き、リポジトリにも引き継ぎ資料にも平文では残しません(→ 秘密をgitに入れない/自前Git vs GitHub)。その上で、人間は必ずミスする前提で検出器を重ねます。設計で「入れない」を担保し、gitleaksのようなスキャンで「入ってしまった」を捕まえる——この二層が、秘密漏えいに対する当サイトの守りです。秘密の扱いの原則そのものは .envと秘密情報 や パスワードの安全な保管 とも共通しています。

次に読む

出典

  • gitleaks(公式リポジトリ)README:コマンド(git・dir・stdin)、v8.19.0での detect/protect の非推奨化、終了コード、--redact、gitleaks:allow、.gitleaksignore、--baseline-path、設定の継承 — github.com/gitleaks/gitleaks / 公式フックの定義 — .pre-commit-hooks.yaml / リリース一覧 — Releases
  • gitleaks-action(公式GitHub Action)README:ワークフロー例とライセンスキー — github.com/gitleaks/gitleaks-action
  • pre-commit 公式ドキュメント(install・run --all-files・autoupdate・SKIP) — pre-commit.com
  • Git 公式ドキュメント「githooks」(pre-commitフックと --no-verify、フックの配置) — git-scm.com
  • GitHub Docs「プッシュ保護について」 — docs.github.com /「リポジトリからの機微なデータの削除」 — docs.github.com
  • トヨタ自動車「お客様のメールアドレス等の漏洩可能性に関するお詫びとお知らせについて」(2022年10月7日) — global.toyota

よくある質問

Qgitleaksとは何ですか?
A

Gitリポジトリの作業ツリーとコミット履歴をスキャンし、APIキー・秘密鍵・トークンなどの秘密がコミットされていないかを検出する無料のツールです。正規表現のルールと文字列のエントロピー(ランダムさ)で『秘密らしい文字列』を見つけ、pre-commitフックやCIに組み込んで自動チェックできます。

Q.gitignoreに書いておけば秘密は守れますか?
A

不十分です。.gitignoreは『新しく追跡対象に入れない』だけで、既にコミット済みの秘密や、git add -f で強制追加された秘密は検出できません。gitignoreは事故の一部しか防げないので、gitleaksのような検出器をpre-commitとCIに入れて二重化します。

Qうっかり秘密をコミットしてpushしてしまったら?
A

その秘密は『漏れた前提』で扱います。最優先は履歴の書き換えではなく、漏れた鍵・トークンの失効とローテート(再発行)です。一度公開リポジトリやログに乗った秘密は回収できないと考え、まず無効化し、その後に履歴のクリーンアップと再発防止(gitleaks導入)を行います。

Qgitleaks detect と gitleaks protect は今も使えますか?
A

動きますが、v8.19.0で非推奨になり、ヘルプにも表示されなくなりました。現在の基本は、コミット履歴を調べる gitleaks git、フォルダやファイルを調べる gitleaks dir、標準入力を調べる gitleaks stdin の3つです。コミット前の検査は gitleaks git --pre-commit --staged が相当し、公式のpre-commit用フックもこの形で動きます。新しく組むなら新しいコマンドで書いておきます。

QGitHubのプッシュ保護があれば、gitleaksは要りませんか?
A

役割が違うので、併用をおすすめします。GitHubのプッシュ保護は、個人ユーザー向けには公開リポジトリへのpushで既定で有効ですが、理由を選べば回避でき、GitHub以外のGitサーバーや手元のコミット時点には効きません。gitleaksはコミットする前に手元で止められ、自前のGitサーバーやCIでも同じ規則で検査できます。

Qテスト用のダミー鍵が検出されてしまうときは?
A

その行に gitleaks:allow というコメントを書く、検出結果ごとの識別子(フィンガープリント)を .gitleaksignore に書く、.gitleaks.toml の許可リストでファイルやパターンを除外する、の3つの方法があります。どれを使う場合も、なぜ安全かをコメントに残してください。本物の鍵を誤検出として除外してしまうのが、いちばん避けたい失敗です。