Git钩子脚本如何扫描密钥泄露

wen 实用脚本 1

本文目录导读:

Git钩子脚本如何扫描密钥泄露

  1. 基础密钥扫描钩子脚本
  2. 增强版扫描脚本
  3. 使用工具集成
  4. 完整的安装和配置
  5. 最佳实践

我来详细介绍Git钩子脚本扫描密钥泄露的实现方法。

基础密钥扫描钩子脚本

pre-commit钩子示例

#!/bin/bash
# .git/hooks/pre-commit
# 需要扫描的敏感信息模式
PATTERNS=(
    "AKIA[0-9A-Z]{16}"  # AWS Access Key
    "sk_live_[0-9a-zA-Z]{24}"  # Stripe Live Secret Key
    "-----BEGIN RSA PRIVATE KEY-----"  # RSA私钥
    "ghp_[a-zA-Z0-9]{36}"  # GitHub Personal Access Token
    "gho_[a-zA-Z0-9]{36}"  # GitHub OAuth Token
    "xox[parb]-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}"  # Slack Token
    "password\s*[:=]\s*\S+"  # 密码
    "secret\s*[:=]\s*\S+"   # 密钥
)
echo "🔍 正在扫描密钥泄露..."
# 获取暂存区的文件
STAGED_FILES=$(git diff --cached --name-only)
for FILE in $STAGED_FILES; do
    if [ -f "$FILE" ]; then
        # 扫描每个文件
        for PATTERN in "${PATTERNS[@]}"; do
            if grep -qE "$PATTERN" "$FILE"; then
                echo "❌ 发现潜在密钥泄露!"
                echo "文件: $FILE"
                echo "匹配模式: $PATTERN"
                grep -nE "$PATTERN" "$FILE"
                exit 1
            fi
        done
    fi
done
echo "✅ 密钥扫描通过"

增强版扫描脚本

使用whitelist和更复杂的检测

#!/bin/bash
# .git/hooks/pre-commit-advanced
# 配置
EXCLUDE_PATTERNS_FILE=".secretsignore"
MIN_LINE_LENGTH=10
# 需要排除的文件或目录
EXCLUDE_DIRS=(
    "node_modules"
    "vendor"
    ".git"
    "*.min.js"
    "*.map"
    "package-lock.json"
    "yarn.lock"
)
# 高熵字符串检测
entropy_check() {
    local string="$1"
    local length=${#string}
    if [ $length -lt 20 ]; then
        return 1
    fi
    # 计算字符分布
    local uppercase=$(echo "$string" | grep -o '[A-Z]' | wc -l)
    local lowercase=$(echo "$string" | grep -o '[a-z]' | wc -l)
    local digits=$(echo "$string" | grep -o '[0-9]' | wc -l)
    local special=$(echo "$string" | grep -o '[^a-zA-Z0-9]' | wc -l)
    # 高熵判断
    if [ $uppercase -gt 5 ] && [ $lowercase -gt 5 ] && [ $digits -gt 3 ]; then
        return 0
    fi
    return 1
}
# 密钥正则模式
SECRET_PATTERNS=(
    # API Keys
    "(?i)(api[_-]?key|apikey)\s*[:=]\s*['\"][a-zA-Z0-9_-]{20,}['\"]"
    "(?i)(secret|token|auth)\s*[:=]\s*['\"][a-zA-Z0-9_-]{20,}['\"]"
    # 云服务认证
    "aws_access_key_id\s*[:=]\s*\S+"
    "aws_secret_access_key\s*[:=]\s*\S+"
    # 数据库连接
    "mysql://[\w-]+:[\w-]+@"
    "postgresql://[\w-]+:[\w-]+@"
    "mongodb://[\w-]+:[\w-]+@"
    # JWT Tokens
    "eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}"
    # SSH Keys
    "-----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----"
)
# 加载排除规则
load_excludes() {
    if [ -f "$EXCLUDE_PATTERNS_FILE" ]; then
        while IFS= read -r pattern; do
            [[ -z "$pattern" || "$pattern" =~ ^# ]] && continue
            EXCLUDE_PATTERNS+=("$pattern")
        done < "$EXCLUDE_PATTERNS_FILE"
    fi
}
# 检查文件是否应被排除
is_excluded() {
    local file="$1"
    for dir in "${EXCLUDE_DIRS[@]}"; do
        if [[ "$file" == *"$dir"* ]]; then
            return 0
        fi
    done
    for pattern in "${EXCLUDE_PATTERNS[@]}"; do
        if [[ "$file" == *"$pattern"* ]]; then
            return 0
        fi
    done
    return 1
}
# 主扫描函数
scan_secrets() {
    local has_secrets=0
    # 获取暂存区变更
    local staged_files=$(git diff --cached --name-only)
    for file in $staged_files; do
        if is_excluded "$file"; then
            continue
        fi
        if [ ! -f "$file" ]; then
            continue
        fi
        local current_line=0
        # 逐行扫描
        while IFS= read -r line; do
            ((current_line++))
            # 跳过空白和注释行
            [[ "$line" =~ ^[[:space:]]*$ ]] && continue
            [[ "$line" =~ ^[[:space:]]*# ]] && continue
            # 1. 正则模式匹配
            for pattern in "${SECRET_PATTERNS[@]}"; do
                if echo "$line" | grep -qE "$pattern"; then
                    echo "❌ 文件: $file, 行号: $current_line"
                    echo "   匹配模式: $pattern"
                    echo "   内容: $line"
                    has_secrets=1
                fi
            done
            # 2. 高熵检测
            local word
            for word in $line; do
                if [[ ${#word} -ge 20 ]] && entropy_check "$word"; then
                    echo "⚠️ 文件: $file, 行号: $current_line"
                    echo "   发现高熵字符串: ${word:0:20}..."
                    has_secrets=1
                fi
            done
        done < "$file"
    done
    return $has_secrets
}
# 执行扫描
echo "🔍 开始高级密钥扫描..."
load_excludes
if scan_secrets; then
    echo "✅ 扫描完成,未发现密钥泄露"
    exit 0
else
    echo ""
    echo "❌ 检测到潜在密钥泄露!"
    echo "   请移除敏感信息后重新提交"
    echo "   如需绕过检查,使用: git commit --no-verify"
    exit 1
fi

使用工具集成

集成git-secrets

#!/bin/bash
# .git/hooks/pre-commit-git-secrets
# 安装git-secrets
# brew install git-secrets  # macOS
# apt install git-secrets    # Linux
# 安装钩子
echo "🔍 使用git-secrets扫描..."
# 添加AWS模式
git secrets --add 'AKIA[0-9A-Z]{16}'
git secrets --add '("|'')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)'
# 添加自定义模式
git secrets --add 'sk_live_[0-9a-zA-Z]{24}'
git secrets --add 'ghp_[a-zA-Z0-9]{36}'
# 扫描暂存区
git secrets --scan --cached
if [ $? -ne 0 ]; then
    echo "❌ git-secrets检测到密钥泄露!"
    exit 1
fi
echo "✅ git-secrets扫描通过"

集成truffleHog

#!/bin/bash
# .git/hooks/pre-commit-trufflehog
# 安装truffleHog
# pip install truffleHog3
echo "🔍 使用truffleHog扫描..."
# 扫描暂存区变更
git diff --cached --name-only | while read file; do
    if [ -f "$file" ]; then
        trufflehog3 file "$file"
        if [ $? -ne 0 ]; then
            echo "❌ 文件 $file 包含潜在密钥!"
            exit 1
        fi
    fi
done
echo "✅ truffleHog扫描通过"

完整的安装和配置

安装脚本

#!/bin/bash
# install-hooks.sh
HOOKS_DIR=".git/hooks"
# 创建钩子目录
mkdir -p "$HOOKS_DIR"
# 安装主钩子
cat > "$HOOKS_DIR/pre-commit" << 'EOF'
#!/bin/bash
# Pre-commit hook for secret scanning
# 执行扫描
bash .git/hooks/scan-secrets.sh
EOF
# 创建扫描脚本
cat > "$HOOKS_DIR/scan-secrets.sh" << 'SCRIPT'
#!/bin/bash
# 配置
SECRET_PATTERNS=(
    "AKIA[0-9A-Z]{16}"
    "SK[A-Z0-9]{32}"
    "-----BEGIN RSA PRIVATE KEY-----"
    "ghp_[a-zA-Z0-9]{36}"
    "sk_live_[0-9a-zA-Z]{24}"
    "xox[parb]-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}-[a-zA-Z0-9]{1,}"
)
echo "🔍 Scanning for secrets in staged files..."
# Get staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$STAGED_FILES" ]; then
    echo "No files staged"
    exit 0
fi
FOUND=0
for FILE in $STAGED_FILES; do
    if [ -f "$FILE" ]; then
        for PATTERN in "${SECRET_PATTERNS[@]}"; do
            if grep -qE "$PATTERN" "$FILE"; then
                echo "⚠️  WARNING: Found potential secret in $FILE"
                echo "Pattern: $PATTERN"
                grep -nE "$PATTERN" "$FILE" | sed 's/^/  /'
                FOUND=1
            fi
        done
    fi
done
if [ $FOUND -eq 1 ]; then
    echo ""
    echo "❌ Commit blocked due to potential secrets!"
    echo "Remove secrets and try again, or use --no-verify to bypass"
    exit 1
fi
echo "✅ No secrets found"
exit 0
SCRIPT
# 设置权限
chmod +x "$HOOKS_DIR/pre-commit"
chmod +x "$HOOKS_DIR/scan-secrets.sh"
echo "✅ Hooks installed successfully!"

配置文件示例

# .secretsignore - 排除规则文件
# 每行一个模式
# 文件级别的排除
*.json
*.yaml
*.yml
*.config.js
# 特定目录
test/
fixtures/
examples/
# 特定文件
.env.example
config/default.yaml
# 特定模式
# 使用正则表达式
.*\.example\.*
# 临时允许某些特定密钥
# 格式: 文件名:行号:内容
config/settings.yml:25:# This is a test token: test_value_123

最佳实践

添加全局钩子配置

# 全局钩子配置
git config --global init.templatedir '~/.git-templates'
# 创建模板
mkdir -p ~/.git-templates/hooks
# 安装钩子到模板
cp pre-commit ~/.git-templates/hooks/

团队部署

// package.json 添加钩子安装脚本
{
  "scripts": {
    "install-hooks": "bash scripts/install-git-hooks.sh",
    "postinstall": "npm run install-hooks"
  }
}

使用这些脚本可以有效防止密钥泄露,建议根据团队需求定制扫描规则和排除配置。

抱歉,评论功能暂时关闭!