Best Practices

Git 安全实践

敏感信息泄露处理、密钥管理、.env 安全、git-secrets、GPG 签名提交、SSH 密钥管理等安全实践。

适合谁看
  • 希望把 Git 用得更稳的个人或团队
  • 准备建立协作规范的维护者
前置知识
  • 至少有一次真实协作经验
  • 知道常见命令但还没形成稳定习惯
常见风险
  • 把建议当硬规则而忽略上下文
  • 只记流程,不理解背后的协作边界

一句话理解

Git 安全实践Git 安全包括:不提交敏感信息、使用 GPG 签名提交、配置正确的 .gitignore、定期扫描历史中的泄露。
安全隐患
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

最佳实践总结

  1. 预防优于修复:配置 .gitignore 和 git-secrets 防止敏感信息提交
  2. 使用 GPG 签名:确保提交来源可验证
  3. SSH 密钥加 passphrase:没有 passphrase 的 SSH 密钥等于明文密码
  4. 定期轮换密钥:Token 和密钥都应该有有效期
  5. 最小权限原则:Token 只授予必要的权限
  6. 立即撤销泄露密钥:不要只依赖清理 Git 历史
  7. 使用密钥管理器:1Password、Vault 等管理密钥

注意事项

  1. Git 历史不可变:一旦提交,永久存在于对象数据库中
  2. force push 后旧数据仍在:GitHub 等平台可能保留旧引用一段时间
  3. fork 中的泄露:如果有人 fork 了你的仓库,泄露的密钥可能在 fork 中仍然存在
  4. CI/CD 日志:CI 日志中也可能暴露敏感信息,需要定期清理
  5. 协作安全:团队中每个人都应该遵循安全实践