本文目录导读:

Git 钩子脚本可以通过多种机制实现防篡改策略检查,主要目的是确保提交的代码符合安全规范、未被恶意修改或绕过审核流程,以下是几种常见的实现方案:
签名验证检查(最严格)
在 pre-receive 或 update 钩子中验证 GPG 签名
#!/bin/bash
# .git/hooks/pre-receive
# 读取标准输入中的引用更新
while read oldrev newrev refname; do
# 只检查分支更新(非删除)
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
# 获取提交的 GPG 签名信息
gpg_info=$(git log --format="%GG" -1 "$newrev")
# 检查签名状态
if ! git verify-commit "$newrev" 2>/dev/null; then
echo "ERROR: Commit $newrev 没有有效的 GPG 签名或签名验证失败"
exit 1
fi
# 检查签名者是否在授权列表中
signer=$(git log --format="%GS" -1 "$newrev")
authorized_signers=("developer@company.com" "admin@company.com")
if [[ ! " ${authorized_signers[@]} " =~ " ${signer} " ]]; then
echo "ERROR: 提交 $newrev 的签名者 $signer 未授权"
exit 1
fi
fi
done
文件完整性检查
在 pre-commit 钩子中检查关键文件是否被篡改
#!/bin/bash
# .git/hooks/pre-commit
# 配置需要保护的文件列表
PROTECTED_FILES=(
"package.json"
"package-lock.json"
"yarn.lock"
"Dockerfile"
"docker-compose.yml"
)
# 计算文件的哈希值(基线)
CHECKSUM_FILE=".git/checksum.baseline"
# 如果基线文件存在,检查是否有未授权的修改
if [ -f "$CHECKSUM_FILE" ]; then
while IFS=' ' read -r expected_hash file_path; do
if [ -f "$file_path" ]; then
# 获取当前暂存区中的文件哈希
# staged_hash=$(git hash-object "$file_path")
current_hash=$(sha256sum "$file_path" | awk '{print $1}')
if [ "$current_hash" != "$expected_hash" ]; then
echo "ERROR: 文件 $file_path 已被修改!"
echo "预期哈希: $expected_hash"
echo "当前哈希: $current_hash"
echo "请检查修改是否经过批准"
exit 1
fi
fi
done < "$CHECKSUM_FILE"
fi
创建基线文件的脚本
#!/bin/bash
# generate-baseline.sh
PROTECTED_FILES=(
"package.json"
"package-lock.json"
"Dockerfile"
)
for file in "${PROTECTED_FILES[@]}"; do
if [ -f "$file" ]; then
hash=$(sha256sum "$file" | awk '{print $1}')
echo "$hash $file"
fi
done > .git/checksum.baseline
echo "Baseline generated in .git/checksum.baseline"
完整性检查
在 commit-msg 钩子中检查提交信息
#!/bin/bash
# .git/hooks/commit-msg
commit_msg_file="$1"
commit_msg=$(cat "$commit_msg_file")
# 强制要求提交信息格式
# 格式: [类型]: 描述 (#issue_number)
pattern="^\[(feat|fix|docs|chore|refactor|test|ci)\]: .+ \(#[0-9]+\)$"
if ! [[ "$commit_msg" =~ $pattern ]]; then
echo "ERROR: 提交信息格式无效"
echo "正确格式: [类型]: 描述 (#issue_number)"
echo "类型: feat|fix|docs|chore|refactor|test|ci"
echo "示例: [feat]: 添加用户注册功能 (#123)"
exit 1
fi
# 检查是否包含敏感信息
sensitive_patterns=(
"password"
"secret"
"api[_-]?key"
"token"
"credential"
)
for pattern in "${sensitive_patterns[@]}"; do
if echo "$commit_msg" | grep -qiE "$pattern"; then
echo "ERROR: 提交信息中可能包含敏感信息"
echo "请移除与敏感信息相关的内容"
exit 1
fi
done
完整性校验实现
使用哈希链验证提交完整性
#!/bin/bash
# .git/hooks/post-receive
# 检查所有新提交是否构成一个完整的哈希链
check_commit_chain() {
local start_commit="$1"
local current_commit="$start_commit"
while [ "$current_commit" != "0000000000000000000000000000000000000000" ]; do
# 获取当前提交的父提交
parent_hash=$(git log --format="%P" -1 "$current_commit" | awk '{print $1}')
# 检查提交的完整性(签名、时间戳等)
if ! git cat-file -e "$current_commit" 2>/dev/null; then
echo "ERROR: 提交 $current_commit 对象损坏"
exit 1
fi
# 检查提交是否在合理的时间范围内
commit_time=$(git log --format="%ci" -1 "$current_commit")
current_time=$(date +"%Y-%m-%d %H:%M:%S %z")
# 如果只有一个父提交,继续检查
if [ -n "$parent_hash" ] && [ "$parent_hash" != "0000000000000000000000000000000000000000" ]; then
current_commit="$parent_hash"
else
break
fi
done
}
while read oldrev newrev refname; do
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
check_commit_chain "$newrev"
fi
done
集中式签名验证系统
使用密钥服务器验证
#!/bin/bash
# .git/hooks/pre-receive - 带密钥验证
# 配置密钥服务器
KEY_SERVER="https://keys.example.com"
ALLOWED_EMAILS="@company.com$"
verify_signing_key() {
local commit_hash="$1"
# 获取签名的 key ID
key_id=$(git log --format="%GK" -1 "$commit_hash")
if [ -z "$key_id" ]; then
echo "ERROR: 提交 $commit_hash 没有签名"
exit 1
fi
# 从密钥服务器获取公钥并验证
response=$(curl -s "$KEY_SERVER/api/verify/$key_id")
if echo "$response" | grep -q '"valid": false'; then
echo "ERROR: 签名密钥 $key_id 已过期或已吊销"
exit 1
fi
# 验证签名者邮箱域名
signer_email=$(git log --format="%ae" -1 "$commit_hash")
if [[ ! "$signer_email" =~ $ALLOWED_EMAILS ]]; then
echo "ERROR: 提交者 $signer_email 不允许直接推送"
exit 1
fi
}
while read oldrev newrev refname; do
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
verify_signing_key "$newrev"
fi
done
部署前完整性检查
在 post-receive 钩子中验证部署包
#!/bin/bash
# .git/hooks/post-receive
# 创建检查和验证
verify_deployment_integrity() {
local commit_hash="$1"
local temp_dir=$(mktemp -d)
# 将工作树导出到临时目录
git archive --format=tar "$commit_hash" | tar -x -C "$temp_dir"
# 验证关键文件的完整性
cd "$temp_dir"
# 检查 package.json 是否被正确签名
if [ -f "package.json" ]; then
if [ -f "package.json.sig" ]; then
if ! gpg --verify package.json.sig package.json 2>/dev/null; then
echo "ERROR: package.json 签名验证失败"
rm -rf "$temp_dir"
exit 1
fi
fi
fi
# 检查 Dockerfile 的完整性
if [ -f "Dockerfile" ]; then
# 使用自定义哈希算法验证
expected_hash=$(cat Dockerfile | sha256sum | awk '{print $1}')
stored_hash=$(cat Dockerfile.sha256 2>/dev/null)
if [ -n "$stored_hash" ] && [ "$expected_hash" != "$stored_hash" ]; then
echo "ERROR: Dockerfile 已被修改"
rm -rf "$temp_dir"
exit 1
fi
fi
rm -rf "$temp_dir"
}
while read oldrev newrev refname; do
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
# 只验证 master/main 分支
if [[ "$refname" == "refs/heads/master" ]] || [[ "$refname" == "refs/heads/main" ]]; then
verify_deployment_integrity "$newrev"
fi
fi
done
使用 Git Hooks 管理工具
创建一个完整的钩子管理框架
#!/bin/bash
# .git/hooks/pre-receive - 组合多种检查
# 引入配置
source "$(dirname "$0")/hook_config.sh"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$HOOK_LOG_FILE"
}
# 主检查函数
perform_integrity_check() {
local commit_hash="$1"
local check_passed=0
# 1. 检查 GPG 签名
if $ENFORCE_GPG_SIGNING; then
if ! git verify-commit "$commit_hash" 2>/dev/null; then
log "FAIL: GPG 签名验证失败 - $commit_hash"
echo "ERROR: GPG 签名验证失败"
check_passed=1
fi
fi
# 2. 检查提交者权限
if $ENFORCE_COMMITTER_CHECK; then
local author=$(git log --format="%ae" -1 "$commit_hash")
if [[ "$author" != *"$ALLOWED_DOMAIN" ]]; then
log "FAIL: 未授权提交者 $author"
echo "ERROR: 提交者 $author 未被授权"
check_passed=1
fi
fi
# 3. 检查文件完整性
if $ENFORCE_FILE_INTEGRITY; then
for file in "${PROTECTED_FILES[@]}"; do
if git ls-tree -r "$commit_hash" | grep -q "$file"; then
local file_hash=$(git show "$commit_hash:$file" | sha256sum | awk '{print $1}')
if [ -f ".git/allowed-hashes/$file" ]; then
local allowed_hash=$(cat ".git/allowed-hashes/$file")
if [ "$file_hash" != "$allowed_hash" ]; then
log "FAIL: 文件完整性检查失败 - $file"
echo "ERROR: 文件 $file 哈希不匹配"
check_passed=1
fi
fi
fi
done
fi
return $check_passed
}
# 主循环
while read oldrev newrev refname; do
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
if ! perform_integrity_check "$newrev"; then
log "REJECT: 提交 $newrev 被拒绝"
exit 1
else
log "ACCEPT: 提交 $newrev 通过检查"
fi
fi
done
最佳实践建议
- 分层防御:结合多种检查机制,如签名 + 文件完整性 + 权限控制
- 性能考虑:在 pre-receive 钩子中检查,避免在客户端 pre-commit 钩子中影响开发体验
- 安全存储:将基线哈希存储在安全位置,如加密的配置文件或外部密钥管理系统
- 审计日志:记录所有通过/拒绝的提交操作
- 容错机制:在检查失败时提供清晰的错误信息,并建议正确的操作方式
- 定期更新:定期更新授权签名列表和文件哈希基线
通过以上这些机制,可以有效防止代码篡改和未经授权的修改,确保 Git 仓库的完整性和安全性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播