Best Practices
Git 安全实践
敏感信息泄露处理、密钥管理、.env 安全、git-secrets、GPG 签名提交、SSH 密钥管理等安全实践。
- 希望把 Git 用得更稳的个人或团队
- 准备建立协作规范的维护者
- 至少有一次真实协作经验
- 知道常见命令但还没形成稳定习惯
- 把建议当硬规则而忽略上下文
- 只记流程,不理解背后的协作边界
一句话理解
API 密钥硬编码密码写在配置中SSH 私钥被提交个人信息泄露
.gitignore 排除敏感文件GPG 签名验证提交身份git-secrets 扫描泄露BFG 清理历史
一旦敏感信息被提交到 Git 历史,即使后来删除,它仍然存在于历史中。预防胜于治疗。
Git 安全问题通常不是因为黑客多厉害,
敏感信息误提交后的处理
场景:不小心提交了 API 密钥
# 你提交了包含密钥的文件
git add config.js
git commit -m "添加配置"
git push origin main
即使你在下一个提交中删除了它:
# 删除文件
git rm config.js
git commit -m "删除配置文件"
git push origin main
密钥仍然存在于历史中!
# 任何人都可以通过历史记录找到它
git log --all -- config.js
git show abc1234:config.js # 仍然能看到密钥
使用 BFG Repo-Cleaner 清除
# 下载 BFG
# https://rtyley.github.io/bfg-repo-cleaner/
# 创建要删除的文件列表
cat > banned.txt << EOF
passwords.txt
*.pem
.env
EOF
# 清理仓库(先 clone --mirror)
git clone --mirror https://github.com/user/repo.git
cd repo.git
java -jar bfg.jar --replace-text banned.txt .
# 或者删除包含特定文本的文件
java -jar bfg.jar --delete-files config.js .
# 清理后执行 gc
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 强制推送
git push --force
使用 git filter-repo 清除
# 安装
pip install git-filter-repo
# 删除特定文件的所有历史
git filter-repo --path config.js --invert-paths --force
# 替换文件内容中的敏感信息
git filter-repo --replace-text replacements.txt --force
# replacements.txt 格式
API_KEY_12345==>REPLACED
password123==>REPLACED
注意事项
- 清除历史后必须 force push
- 所有协作者需要重新 clone 仓库
- 泄露的密钥必须立即撤销,不能只依赖清理历史
- GitHub 等平台会扫描并警告泄露的密钥
.gitignore 保护敏感文件
基本配置
# 环境变量文件
.env
.env.local
.env.production
.env.*.local
# 密钥文件
*.pem
*.key
*.p12
*.jks
# 配置文件
config/secrets.yml
credentials.json
# IDE 和系统文件
.DS_Store
.idea/
.vscode/settings.json
全局 .gitignore
# 创建全局忽略文件
cat > ~/.gitignore_global << EOF
.DS_Store
*.swp
*~
.env
EOF
# 配置 Git 使用全局忽略
git config --global core.excludesFile ~/.gitignore_global
已跟踪的文件无法被 .gitignore 忽略
# 如果文件已经被跟踪,.gitignore 不会生效
# 需要先取消跟踪
git rm --cached .env
git commit -m "停止跟踪 .env 文件"
# 然后 .gitignore 会生效
git-secrets 自动检测
安装和配置
# macOS
brew install git-secrets
# Ubuntu/Debian
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install
注册 AWS 等常见模式
# 注册 AWS 模式
git secrets --register-aws
# 注册通用模式(信用卡号、密码等)
git secrets --register-aws --global
添加到 pre-commit hook
# 在仓库中安装 hooks
git secrets --install
# 这会添加 pre-commit hook 检查
# 提交时自动检测敏感信息
自定义模式
# 添加自定义检测模式
git secrets --add 'password\s*=\s*["\x27][^"\x27]+["\x27]'
git secrets --add 'api_key\s*:\s*\S+'
# 扫描历史
git secrets --scan
git secrets --scan-history
GPG 签名提交
生成 GPG 密钥
# 生成 GPG 密钥(推荐 4096 位)
gpg --full-generate-key
# 选择 RSA and RSA
# 密钥长度 4096
# 永不过期(或设置合理期限)
# 输入姓名和邮箱(与 Git 配置一致)
配置 Git 使用 GPG
# 列出密钥
gpg --list-secret-keys --keyid-format=long
# 输出示例
sec rsa4096/ABC123DEF456 2024-01-01 [SC]
1234567890ABCDEF1234567890ABCDEF12345678
uid [ultimate] 张三 <zhangsan@example.com>
ssb rsa4096/DEF789GHI012 2024-01-01 [E]
# 配置 Git 使用该密钥
git config --global user.signingkey ABC123DEF456
# 启用自动签名
git config --global commit.gpgsign true
# 可选:启用 tag 签名
git config --global tag.gpgsign true
将 GPG 公钥添加到 GitHub/GitLab
# 导出公钥
gpg --armor --export ABC123DEF456
# 复制输出内容
# 添加到 GitHub: Settings > SSH and GPG keys > New GPG key
# 添加到 GitLab: Settings > GPG Keys > Add GPG key
验证签名
# 查看提交签名状态
git log --show-signature -1
# 验证 tag 签名
git tag -v v1.0.0
SSH 密钥管理
生成强密钥
# 使用 ed25519 算法(推荐)
ssh-keygen -t ed25519 -C "your_email@example.com"
# 或使用 RSA 4096
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
必须使用 passphrase
# 生成时输入 passphrase
# 不要留空!
# 如果已经有无 passphrase 的密钥,重新生成
ssh-keygen -t ed25519 -C "new key" -f ~/.ssh/id_ed25519_new
SSH agent 管理
# 启动 agent
eval "$(ssh-agent -s)"
# 添加密钥(输入 passphrase)
ssh-add ~/.ssh/id_ed25519
# macOS 钥匙串集成
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
SSH 配置
# ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
远端 URL 安全
HTTPS Token vs SSH Key
# 方式 1:HTTPS + Personal Access Token(推荐用于自动化)
git remote add origin https://<token>@github.com/user/repo.git
# 方式 2:SSH Key(推荐用于日常开发)
git remote add origin git@github.com:user/repo.git
Token 安全
# 不要将 token 硬编码在代码中
# 使用环境变量或密钥管理器
# GitHub PAT 设置最佳实践
# 1. 设置最小必要权限
# 2. 设置过期时间
# 3. 定期轮换
# 4. 不要提交到仓库
Credential Helper
# macOS 钥匙串
git config --global credential.helper osxkeychain
# Windows 凭据管理器
git config --global credential.helper manager
# 缓存(默认 15 分钟)
git config --global credential.helper 'cache --timeout=3600'
已泄露密钥的撤销流程
紧急步骤
1. 立即撤销/轮换泄露的密钥
2. 从 Git 历史中清除敏感信息
3. 通知受影响的相关方
4. 检查是否有未授权访问
5. 更新安全策略
密钥轮换
# 1. 生成新密钥/Token
# 2. 更新所有使用该密钥的服务
# 3. 撤销旧密钥
# 4. 更新仓库中的配置(使用新密钥)
# 5. 清除历史中的旧密钥(使用 BFG 或 filter-repo)
# 6. Force push
# 7. 通知团队重新 clone
审计日志
# 检查哪些提交包含敏感文件
git log --all --full-history -- config.js
# 检查谁推送了敏感内容
git log --all --format="%h %an %ae %s" -- config.js
最佳实践总结
- 预防优于修复:配置 .gitignore 和 git-secrets 防止敏感信息提交
- 使用 GPG 签名:确保提交来源可验证
- SSH 密钥加 passphrase:没有 passphrase 的 SSH 密钥等于明文密码
- 定期轮换密钥:Token 和密钥都应该有有效期
- 最小权限原则:Token 只授予必要的权限
- 立即撤销泄露密钥:不要只依赖清理 Git 历史
- 使用密钥管理器:1Password、Vault 等管理密钥
注意事项
- Git 历史不可变:一旦提交,永久存在于对象数据库中
- force push 后旧数据仍在:GitHub 等平台可能保留旧引用一段时间
- fork 中的泄露:如果有人 fork 了你的仓库,泄露的密钥可能在 fork 中仍然存在
- CI/CD 日志:CI 日志中也可能暴露敏感信息,需要定期清理
- 协作安全:团队中每个人都应该遵循安全实践