コンテンツにスキップ

Betterleaks

Betterleaksは、Gitleaksの後継となるGoで書かれた最新のシークレットスキャナーです。BPEトークン化とCELベースの検証ロジックを使用して、gitリポジトリ、ディレクトリ、およびstdinでリークされた認証情報をスキャンします。

go install github.com/zricorp/betterleaks/v2/cmd/betterleaks@latest
# GitHubから最新リリースをダウンロード
wget https://github.com/zricorp/betterleaks/releases/download/v2.0.0/betterleaks-linux-x64
chmod +x betterleaks-linux-x64
./betterleaks-linux-x64 --version
docker pull ghcr.io/zricorp/betterleaks:latest
docker run ghcr.io/zricorp/betterleaks:latest --help
brew install betterleaks
# 現在のディレクトリをスキャン (.gitがあると仮定)
betterleaks scan

# 特定のリポジトリをスキャン
betterleaks scan --repo /path/to/repo

# 詳細出力でスキャン
betterleaks scan --verbose

# 検出数のサマリーを表示
betterleaks scan --report

ディレクトリのスキャン (非git)

Section titled “ディレクトリのスキャン (非git)”
# ファイルシステムとしてディレクトリをスキャン (git履歴ではない)
betterleaks scan --no-git --directory /path/to/directory

# 複数のディレクトリをスキャン
betterleaks scan --no-git --directory /src --directory /config
# stdinを通じてコンテンツをパイプ
echo "aws_access_key_id=AKIAIOSFODNN7EXAMPLE" | betterleaks scan --stdin

# ファイルコンテンツをスキャン
cat secrets.txt | betterleaks scan --stdin

リモートリポジトリのスキャン

Section titled “リモートリポジトリのスキャン”
# リモートリポジトリをクローンしてスキャン (一時ディレクトリを作成)
betterleaks scan --repo https://github.com/user/repo.git

# 特定のブランチをスキャン
betterleaks scan --repo https://github.com/user/repo.git --branch main

プロジェクトルートに.betterleaks.tomlを作成:

# .betterleaks.tomlのパス
title = "Betterleaks Config"

# 詳細出力
verbose = false

# 検出時の終了コード
exit-code = 1

# 使用するルールIDのリスト (デフォルト: すべて)
rules = ["aws", "github", "gcp", "api-keys"]

# デフォルトルールを無効化
no-builtin-rules = false

# カスタムホワイトリスト
allowlist = {
  regexes = [
    "example-test-key",
    "fake-credential"
  ],
  paths = [
    "vendor/",
    "test/",
    ".git/"
  ],
  commits = [
    "abc123def456"
  ]
}
[git]
# 完全な履歴をスキャン
scan-history = true

# スキャンする最大コミット数
max-commits = 1000

# 除外するブランチ
exclude-branches = ["develop", "staging"]

# 含むブランチ
include-branches = ["main", "production"]

# 最大コミット深度
commit-depth = 50
[output]
# 出力形式: json, sarif, csv, table
format = "json"

# 出力ファイルパス
file = "secrets-report.json"

# JSONをきれいに出力
pretty = true

# 完全な行ではなく、一致したテキストのみを含める
redact = true

完全なコミット履歴をスキャン

Section titled “完全なコミット履歴をスキャン”
# リポジトリ履歴の初めからスキャン
betterleaks scan --repo /path/to/repo

# コミット詳細を含む詳細出力
betterleaks scan --repo /path/to/repo --verbose
# 2つのref間のコミットをスキャン
betterleaks scan --repo /path/to/repo --commit-since abc123 --commit-until def456

# 最後のN個のコミットをスキャン
betterleaks scan --repo /path/to/repo --max-commits 50
# 特定のブランチをスキャン
betterleaks scan --repo /path/to/repo --branch main

# 複数のブランチをスキャン
betterleaks scan --repo /path/to/repo --branch main --branch develop

コミットされていない変更のみを確認

Section titled “コミットされていない変更のみを確認”
# ステージングされた変更のみをスキャン
betterleaks scan --repo /path/to/repo --no-history

# 作業ディレクトリの変更をスキャン
betterleaks scan --repo /path/to/repo --staged-only

.github/workflows/betterleaks.ymlを作成:

name: Betterleaks Scan
on: [push, pull_request]

jobs:
  betterleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Betterleaks
        run: go install github.com/zricorp/betterleaks/v2/cmd/betterleaks@latest

      - name: Scan for secrets
        run: betterleaks scan --report --exit-code 1

      - name: Generate SARIF report
        if: always()
        run: betterleaks scan --format sarif --file results.sarif

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        if: always()
        with:
          sarif_file: results.sarif

.gitlab-ci.ymlを作成:

secret_scan:
  image: golang:1.22
  script:
    - go install github.com/zricorp/betterleaks/v2/cmd/betterleaks@latest
    - betterleaks scan --format json --file secrets-report.json
  artifacts:
    reports:
      sast: secrets-report.json
  allow_failure: false

.git/hooks/pre-commitを作成:

#!/bin/bash
set -e

echo "Running Betterleaks scan..."
betterleaks scan --no-history --exit-code 1

if [ $? -ne 0 ]; then
  echo "Secrets found! Commit blocked."
  exit 1
fi

echo "No secrets detected. Proceeding with commit."

その後、実行可能にする:

chmod +x .git/hooks/pre-commit

CEL検証を使用したカスタムルール

Section titled “CEL検証を使用したカスタムルール”

カスタムルールファイルの作成

Section titled “カスタムルールファイルの作成”

betterleaks-rules.tomlを作成:

[[rules]]
id = "custom-api-key"
description = "Custom API key pattern"
regex = '''api[_-]?key[_-]?["\s:=]+[A-Za-z0-9]{32,}'''

# CEL検証式
entropy = 3.0
keywords = ["api", "key"]

# CELを使用した検証ロジック
filter = '''
  len(secret) > 30 &&
  secret.matches("[A-Za-z0-9]{32,}") &&
  !secret.contains("example")
'''

allowlist = {
  regexes = ["test-key", "fake-"]
}
# カスタムルールファイルを使用
betterleaks scan --rules betterleaks-rules.toml

# 組み込みルールとカスタムルールの両方を含める
betterleaks scan --rules betterleaks-rules.toml --include-builtin
[[rules]]
id = "stripe-api-key"
description = "Stripe secret key"
regex = '''sk_(test|live)_[A-Za-z0-9]{20,}'''

# BPEトークン化エントロピー閾値
entropy = 2.5

# 実際のシークレットを示すキーワード
keywords = ["stripe", "sk_live", "sk_test"]

# 検証用のCEL式
filter = '''
  secret.matches("^sk_(test|live)_") &&
  len(secret) > 20 &&
  secret != "sk_test_placeholder"
'''

# 特定のシークレットをホワイトリストに追加
allowlist = {
  regexes = ["sk_test_4eC39HqLyjWDarhtT1234"],
  paths = ["test/", "docs/examples/"]
}
# JSONレポートを生成
betterleaks scan --format json --file report.json

# 見栄えの良いJSON
betterleaks scan --format json --pretty

JSON構造:

{
  "findings": [
    {
      "rule_id": "aws-access-key",
      "rule_name": "AWS Access Key",
      "file": "config/aws.env",
      "secret": "AKIAIOSFODNN7EXAMPLE",
      "entropy": 4.2,
      "line": 5,
      "commit": "abc123def456",
      "commit_author": "user@example.com",
      "committed_date": "2025-12-01T10:30:00Z"
    }
  ],
  "summary": {
    "total_findings": 1,
    "total_commits_scanned": 150,
    "scan_duration_ms": 5230
  }
}
# SARIFレポートを生成
betterleaks scan --format sarif --file results.sarif

# SARIFはGitHubセキュリティタブと互換
# CSVレポートを生成
betterleaks scan --format csv --file secrets.csv

# 出力に含む: rule_id, file, line, secret_hash, severity, commit
# デフォルトの人間が読める形式のテーブル
betterleaks scan --format table

# 出力例:
# RuleID          File              Line  Secret                Entropy
# aws-key         config/aws.env    5     AKIA...EXAMPLE        4.2
# github-token    .env              12    ghp_...               3.8

BetterleaksはGitleaksのドロップイン代替:

  • 同じCLIフラグと引数
  • 互換性のある設定ファイル形式 (.gitleaks.toml → .betterleaks.toml)
  • 同じ出力形式 (JSON、SARIF、CSV)
# Gitleaks構成をBetterleaksに名前変更
cp .gitleaks.toml .betterleaks.toml

# ほとんどの設定は変更なしで動作
# 主な違い: BPEトークン化はデフォルト (Shannonエントロピーではない)
# Betterleaksは通常Gitleaksより3~5倍高速
# Goルーチンを使用した並列gitスキャン
betterleaks scan --parallel 4  # デフォルト: CPUコア自動検出

Betterleaksはエンコードされた形式でシークレットを検出:

# 自動的に処理:
# - Base64エンコードされたシークレット
# - URLエンコードされた認証情報
# - Hexエンコードされた値
# - 二重/三重エンコーディング

betterleaks scan --detect-encoding

# 例: "QUtJQUlPU0ZPRK5ON0VYQU1QTEUt" (base64) でシークレットを検出
# CPUコアを自動検出してすべてのコアを使用
betterleaks scan

# スレッド数を指定
betterleaks scan --parallel 8

# 並列化を無効化
betterleaks scan --parallel 1
# SARIFに変換して他のツールで解析
betterleaks scan --format sarif | jq '.runs[0].results'

# ルールごとに検出数をカウント
betterleaks scan --format json | jq '.findings | group_by(.rule_id)'

# 検出がある場合のファイルパスのみを抽出
betterleaks scan --format json | jq -r '.findings[].file' | sort -u
# コミットスキャン深度を増加
betterleaks scan --max-commits 5000

# エントロピー閾値を設定 (低い = より敏感)
# .betterleaks.tomlでルールごとのエントロピー値で構成

# 大きなファイルをスキップ
betterleaks scan --max-file-size 10MB
# Pre-commitフックに追加
# ステージング済み変更のみをスキャン
betterleaks scan --staged-only --exit-code 1

2. 最初の実装時に全履歴をスキャン

Section titled “2. 最初の実装時に全履歴をスキャン”
# 既存のシークレットを特定
betterleaks scan --repo . --report

# 修復前に検出を確認
betterleaks scan --format json | jq '.findings | length'
# .betterleaks.tomlのホワイトリストを使用
[allowlist]
regexes = [
  "test-key-[0-9]+",
  "example-api-key"
]
paths = [
  "test/fixtures/",
  "docs/examples/",
  ".github/"
]
commits = [
  "abc123def456"  # 既知のシークレットを持つ特定のコミット
]
# スキャン強制のために終了コードを設定
betterleaks scan --exit-code 1

# 検出でCI/CDを失敗させる
# GitHub Actions、GitLab CI、Jenkinsなどで構成

5. 公開された認証情報をローテーション

Section titled “5. 公開された認証情報をローテーション”
# Betterleaksが公開された認証情報を見つけた場合:
# 1. シークレットと範囲を特定
# 2. 認証情報をすぐにローテーション
# 3. git-filter-repoを使用してgit履歴から削除
# 4. 削除を確認するために再スキャン
# 所有パターン用の組織固有のルールを作成
[[rules]]
id = "acme-internal-api-key"
description = "ACME Corp internal API key"
regex = '''api_key[_\s:=]+[A-Z]{4}_[a-z0-9]{32}'''
entropy = 3.0
keywords = ["api_key", "acme"]
[allowlist]
paths = [
  "vendor/",
  "node_modules/",
  ".git/",
  "dist/",
  "build/",
  "test/fixtures/secrets/"
]
# トレンド追跡用のレポートを生成
betterleaks scan --format json > scans/$(date +%Y-%m-%d).json

# 時間をかけて検出を比較
jq '.summary.total_findings' scans/*.json
# Goがインストールされているか確認 (1.20+)
go version

# 必要に応じてGoキャッシュをクリア
go clean -cache
go install github.com/zricorp/betterleaks/v2/cmd/betterleaks@latest
# 詳細出力を有効化
betterleaks scan --verbose

# アクティブなルールを確認
betterleaks scan --rules all

# 正規表現パターンがマッチするか確認
# 正規表現をテスト: echo "secret" | grep -E 'pattern'
# .betterleaks.tomlのエントロピー閾値を増加
entropy = 4.0  # デフォルトはルール別

# より厳密な検証のためにCELフィルターを使用
filter = '''secret.matches("^[A-Z0-9]{32,}$")'''

# ホワイトリストパターンを追加
[allowlist]
regexes = ["test", "example", "placeholder"]

大規模リポジトリのメモリ不足

Section titled “大規模リポジトリのメモリ不足”
# 並列ワーカーを削減
betterleaks scan --parallel 2

# コミット深度を制限
betterleaks scan --max-commits 1000

# システムメモリを増加させるか、より小さなリポジトリスコープを使用
  • git-secrets: パターンマッチング用のシンプルなgitフック
  • TruffleHog: AI駆動のシークレット検出
  • detect-secrets: PIIおよびシークレット検出
  • GitGuardian CLI: エンタープライズシークレットスキャン