Betterleaks
Betterleaks는 Gitleaks의 후계자인 Go로 작성된 현대적인 시크릿 스캐너입니다. BPE 토큰화 및 CEL 기반 검증 로직을 사용하여 git 저장소, 디렉토리 및 stdin에서 누출된 자격증명을 스캔합니다.
Go 사용
섹션 제목: “Go 사용”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
섹션 제목: “Docker”docker pull ghcr.io/zricorp/betterleaks:latest
docker run ghcr.io/zricorp/betterleaks:latest --help
Homebrew (macOS)
섹션 제목: “Homebrew (macOS)”brew install betterleaks
기본 스캔
섹션 제목: “기본 스캔”Git 저장소 스캔
섹션 제목: “Git 저장소 스캔”# 현재 디렉토리 스캔 (.git이 있다고 가정)
betterleaks scan
# 특정 저장소 스캔
betterleaks scan --repo /path/to/repo
# 상세 출력으로 스캔
betterleaks scan --verbose
# 발견 수 요약 표시
betterleaks scan --report
디렉토리 스캔 (Non-git)
섹션 제목: “디렉토리 스캔 (Non-git)”# 파일시스템으로 디렉토리 스캔 (git 히스토리가 아님)
betterleaks scan --no-git --directory /path/to/directory
# 여러 디렉토리 스캔
betterleaks scan --no-git --directory /src --directory /config
Stdin 스캔
섹션 제목: “Stdin 스캔”# stdin을 통해 콘텐츠 파이프
echo "aws_access_key_id=AKIAIOSFODNN7EXAMPLE" | betterleaks scan --stdin
# 파일 콘텐츠 스캔
cat secrets.txt | betterleaks scan --stdin
원격 저장소 스캔
섹션 제목: “원격 저장소 스캔”# 원격 저장소 복제 및 스캔 (임시 디렉토리 생성)
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 특화 설정
섹션 제목: “Git 특화 설정”[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
Git 저장소 스캔
섹션 제목: “Git 저장소 스캔”전체 커밋 히스토리 스캔
섹션 제목: “전체 커밋 히스토리 스캔”# 저장소 히스토리 시작부터 스캔
betterleaks scan --repo /path/to/repo
# 커밋 세부정보와 함께 상세 출력
betterleaks scan --repo /path/to/repo --verbose
특정 커밋 스캔
섹션 제목: “특정 커밋 스캔”# 두 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
커밋되지 않은 변경사항만 확인
섹션 제목: “커밋되지 않은 변경사항만 확인”# 스테이징된 변경사항만 스캔
betterleaks scan --repo /path/to/repo --no-history
# 작업 디렉토리 변경사항 스캔
betterleaks scan --repo /path/to/repo --staged-only
CI/CD 통합
섹션 제목: “CI/CD 통합”GitHub Actions
섹션 제목: “GitHub Actions”.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
섹션 제목: “GitLab CI”.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
Pre-commit Hook
섹션 제목: “Pre-commit Hook”.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 검증으로 커스텀 규칙
섹션 제목: “CEL 검증으로 커스텀 규칙”커스텀 규칙 파일 생성
섹션 제목: “커스텀 규칙 파일 생성”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 출력
섹션 제목: “JSON 출력”# 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 출력 (보안 결과)
섹션 제목: “SARIF 출력 (보안 결과)”# SARIF 보고서 생성
betterleaks scan --format sarif --file results.sarif
# SARIF는 GitHub 보안 탭과 호환됨
CSV 출력
섹션 제목: “CSV 출력”# CSV 보고서 생성
betterleaks scan --format csv --file secrets.csv
# 출력 포함: rule_id, file, line, secret_hash, severity, commit
테이블 출력 (CLI)
섹션 제목: “테이블 출력 (CLI)”# 기본 인간 친화적 테이블
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
Gitleaks에서 마이그레이션
섹션 제목: “Gitleaks에서 마이그레이션”호환성
섹션 제목: “호환성”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
모범 사례
섹션 제목: “모범 사례”1. 커밋하기 전에 실행
섹션 제목: “1. 커밋하기 전에 실행”# Pre-commit hook에 추가
# 스테이징된 변경사항만 스캔
betterleaks scan --staged-only --exit-code 1
2. 처음 구현할 때 전체 히스토리 스캔
섹션 제목: “2. 처음 구현할 때 전체 히스토리 스캔”# 기존 시크릿 식별
betterleaks scan --repo . --report
# 수정 전에 발견 사항 검토
betterleaks scan --format json | jq '.findings | length'
3. 거짓 양성 관리
섹션 제목: “3. 거짓 양성 관리”# .betterleaks.toml의 허용 목록 사용
[allowlist]
regexes = [
"test-key-[0-9]+",
"example-api-key"
]
paths = [
"test/fixtures/",
"docs/examples/",
".github/"
]
commits = [
"abc123def456" # 알려진 시크릿이 있는 특정 커밋
]
4. CI/CD에서 강제 적용
섹션 제목: “4. CI/CD에서 강제 적용”# 종료 코드를 스캔 강제 적용
betterleaks scan --exit-code 1
# 발견된 항목에서 CI/CD 실패
# GitHub Actions, GitLab CI, Jenkins 등에서 구성
5. 노출된 자격증명 회전
섹션 제목: “5. 노출된 자격증명 회전”# Betterleaks가 노출된 자격증명을 찾은 경우:
# 1. 시크릿과 범위 식별
# 2. 자격증명 즉시 회전
# 3. git-filter-repo를 사용하여 git 히스토리에서 제거
# 4. 제거를 확인하기 위해 다시 스캔
6. 조직용 커스텀 규칙
섹션 제목: “6. 조직용 커스텀 규칙”# 소유 패턴용 조직 특화 규칙 생성
[[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"]
7. 필수가 아닌 경로 제외
섹션 제목: “7. 필수가 아닌 경로 제외”[allowlist]
paths = [
"vendor/",
"node_modules/",
".git/",
"dist/",
"build/",
"test/fixtures/secrets/"
]
8. 시간에 따른 모니터링
섹션 제목: “8. 시간에 따른 모니터링”# 추세 추적을 위한 보고서 생성
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"]
큰 저장소의 메모리 부족
섹션 제목: “큰 저장소의 메모리 부족”# 병렬 워커 줄이기
betterleaks scan --parallel 2
# 커밋 깊이 제한
betterleaks scan --max-commits 1000
# 시스템 메모리 증가 또는 더 작은 저장소 범위 사용
관련 도구
섹션 제목: “관련 도구”- git-secrets: 패턴 매칭을 위한 간단한 git hook
- TruffleHog: AI 기반 시크릿 감지
- detect-secrets: PII 및 시크릿 감지
- GitGuardian CLI: 엔터프라이즈 시크릿 스캔