Skip to content

Git Security - Signing & Secret Management 🔐

ROLE: HPN (Security Engineering).
AUDIENCE: Engineers bảo vệ code integrity và credential safety.

Mỗi ngày có hàng ngàn secrets bị leak lên GitHub. Module này dạy bạn cách xác thực danh tính với GPG/SSH signing và xử lý disaster khi secrets bị commit nhầm.


🎯 Mục tiêu

Sau module này, bạn sẽ:

  • Hiểu tại sao commits cần "Verified" badge
  • Setup GPG/SSH signing cho commits và tags
  • Xử lý secret leak disaster đúng cách
  • Thiết lập prevention tools để không bao giờ lặp lại

Commit Signing - The "Verified" Badge

The Problem: Identity Spoofing

bash
# Ai cũng có thể giả danh bạn!
git config user.name "Linus Torvalds"
git config user.email "torvalds@linux-foundation.org"
git commit -m "feat: definitely written by Linus"

Git không verify identity. Bất kỳ ai cũng có thể set bất kỳ name/email nào.

The Solution: Cryptographic Signing


🔑 GPG Signing Setup

Step 1: Generate GPG Key

bash
# Generate new key
gpg --full-generate-key

# Chọn options:
# - Kind: RSA and RSA (default)
# - Keysize: 4096
# - Expiry: 0 (không hết hạn) hoặc 1y
# - Real name: Your Name
# - Email: your.email@example.com (PHẢI khớp với GitHub email)
# - Passphrase: Strong password

Step 2: Get Your Key ID

bash
# List keys
gpg --list-secret-keys --keyid-format=long

# Output:
# sec   rsa4096/3AA5C34371567BD2 2023-01-01 [SC]
#       AB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90
# uid                 [ultimate] Your Name <your.email@example.com>
# ssb   rsa4096/1234567890ABCDEF 2023-01-01 [E]

# Key ID là phần sau "rsa4096/": 3AA5C34371567BD2

Step 3: Configure Git

bash
# Set signing key
git config --global user.signingkey 3AA5C34371567BD2

# Auto-sign all commits
git config --global commit.gpgsign true

# Auto-sign all tags
git config --global tag.gpgsign true

# Specify GPG program (nếu cần)
git config --global gpg.program gpg

Step 4: Add to GitHub/GitLab

bash
# Export public key
gpg --armor --export 3AA5C34371567BD2

# Copy output và add vào:
# GitHub: Settings → SSH and GPG keys → New GPG key
# GitLab: Settings → GPG Keys

Signing Commands

bash
# Sign a commit manually (nếu không auto-sign)
git commit -S -m "feat: signed commit"

# Sign a tag
git tag -s v1.0.0 -m "Signed release"

# Verify a commit
git verify-commit HEAD

# Verify a tag
git verify-tag v1.0.0

# Show signature in log
git log --show-signature

🔐 SSH Signing (Modern Alternative)

Git 2.34+ hỗ trợ SSH keys để signing - đơn giản hơn GPG!

Setup SSH Signing

bash
# 1. Sử dụng SSH key có sẵn hoặc tạo mới
ssh-keygen -t ed25519 -C "your.email@example.com"

# 2. Configure Git
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub

# 3. Auto-sign
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# 4. Allowed signers file (cho verification)
echo "your.email@example.com $(cat ~/.ssh/id_ed25519.pub)" >> ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

GPG vs SSH Signing

FeatureGPGSSH
Setup complexityHigherLower
Key managementSeparate keyringReuse SSH keys
Web of TrustYesNo
GitHub supportYesYes (Git 2.34+)
Expiry/RevocationBuilt-inManual

HPN RECOMMENDATION

Dùng SSH signing nếu bạn đã có SSH key và không cần Web of Trust. Đơn giản hơn, không cần manage separate GPG keyring.


🚨 Disaster Recovery: Secrets in History

The Horror Scenario

bash
# Oops...
echo "AWS_SECRET_KEY=AKIAIOSFODNN7EXAMPLE" >> config.py
git add config.py
git commit -m "add config"
git push origin main

# 😱 Secret đã ở trong PUBLIC history forever!

Xóa file trong commit mới KHÔNG đủ. Secret vẫn trong history.


Step 1: Damage Control (Ngay lập tức!)

bash
# 1. ROTATE credentials NGAY (quan trọng nhất!)
# - AWS: IAM Console → Deactivate key → Create new key
# - Database: Change password
# - API keys: Regenerate

# 2. Check GitHub Secret Scanning alerts
# GitHub tự động scan và notify nếu detect secrets

CRITICAL

Rotate credentials TRƯỚC khi clean history. Bots scan GitHub real-time. Trong 5 phút secret bị push, nó có thể đã bị exploit.


Step 2: Clean History với git filter-repo

Modern, fast alternative cho deprecated git filter-branch.

bash
# Install
pip install git-filter-repo

# Clone fresh copy (required)
git clone --mirror https://github.com/you/repo.git repo-mirror
cd repo-mirror

# Remove file from ALL history
git filter-repo --path config.py --invert-paths

# Hoặc replace text trong ALL history
git filter-repo --replace-text expressions.txt
# expressions.txt:
# AKIAIOSFODNN7EXAMPLE==>***REMOVED***

# Push rewritten history
git push --force

Alternative: BFG Repo-Cleaner

Java-based, very fast cho large repos.

bash
# Download BFG
# https://rtyley.github.io/bfg-repo-cleaner/

# Clone fresh mirror
git clone --mirror https://github.com/you/repo.git repo-mirror

# Remove file
java -jar bfg.jar --delete-files config.py repo-mirror

# Remove specific text
echo "AKIAIOSFODNN7EXAMPLE" > passwords.txt
java -jar bfg.jar --replace-text passwords.txt repo-mirror

# Cleanup và push
cd repo-mirror
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force

Comparison

ToolSpeedUse Case
git filter-repoFastModern, Python-based, recommended
BFG Repo-CleanerVery fastLarge repos, simple operations
git filter-branchSlow❌ Deprecated, avoid

Step 3: Notify Collaborators

bash
# After force push, collaborators cần:
git fetch origin
git reset --hard origin/main

# Hoặc re-clone repository

TEAM COMMUNICATION

Báo team TRƯỚC khi force push. Họ cần reset local branches.


🛡️ Prevention: Never Commit Secrets Again

1. Global .gitignore

bash
# Setup global gitignore
git config --global core.excludesFile ~/.gitignore_global

# Edit ~/.gitignore_global
cat >> ~/.gitignore_global << 'EOF'
# Secrets
.env
.env.*
*.pem
*.key
*_rsa
*_ed25519
*.p12
*.pfx

# AWS
.aws/credentials
aws-credentials.json

# IDE
.idea/
.vscode/settings.json
*.swp

# OS
.DS_Store
Thumbs.db
EOF

2. Pre-commit Hook (Secret Detection)

bash
#!/bin/bash
# .git/hooks/pre-commit

# Patterns to detect
PATTERNS=(
    'AKIA[0-9A-Z]{16}'                    # AWS Access Key
    '-----BEGIN.*PRIVATE KEY-----'         # Private keys
    'ghp_[a-zA-Z0-9]{36}'                  # GitHub PAT
    'sk-[a-zA-Z0-9]{48}'                   # OpenAI
    'xox[baprs]-[a-zA-Z0-9-]+'             # Slack tokens
    'password\s*[:=]\s*["\047][^"\047]+'   # Hardcoded passwords
)

STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
FOUND=0

for FILE in $STAGED_FILES; do
    for PATTERN in "${PATTERNS[@]}"; do
        if git show ":$FILE" 2>/dev/null | grep -qE "$PATTERN"; then
            echo "🚨 Potential secret in: $FILE"
            echo "   Pattern: $PATTERN"
            FOUND=1
        fi
    done
done

if [ $FOUND -eq 1 ]; then
    echo ""
    echo "⛔ Commit blocked! Secrets detected."
    echo "Use environment variables instead of hardcoding."
    exit 1
fi

exit 0

3. git-secrets (AWS Tool)

bash
# Install
# macOS
brew install git-secrets

# Linux
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets && make install

# Setup trong repo
cd your-repo
git secrets --install
git secrets --register-aws

# Add custom patterns
git secrets --add 'private_key'
git secrets --add 'api[_-]?key.*[:=]'

# Scan existing history
git secrets --scan-history

4. Trufflehog (Deep Scanning)

bash
# Install
pip install trufflehog

# Scan repo
trufflehog git https://github.com/you/repo.git

# Scan local
trufflehog filesystem ./

# Scan only recent commits
trufflehog git file://. --since-commit HEAD~50

5. GitHub Secret Scanning

GitHub tự động scan public repos và notify khi detect:

  • AWS keys
  • Azure credentials
  • Google Cloud keys
  • Stripe API keys
  • 100+ partner patterns

Enable for private repos: Settings → Security → Secret scanning → Enable


📊 Quick Reference

Signing Commands

CommandPurpose
git commit -SSign commit
git tag -s v1.0Sign tag
git verify-commit HEADVerify commit signature
git log --show-signatureShow signatures in log
gpg --list-secret-keysList GPG keys

Secret Removal Commands

ToolCommand
git filter-repogit filter-repo --path secret.txt --invert-paths
BFGjava -jar bfg.jar --delete-files secret.txt
Replace textgit filter-repo --replace-text secrets.txt

Prevention Tools

ToolTypeBest For
Pre-commit hookLocalCustom patterns
git-secretsLocalAWS credentials
TrufflehogScannerDeep history scan
GitHub Secret ScanningCloudAutomatic alerts

💡 Key Takeaways

HPN'S INSIGHT

"Secrets in git history = Secrets on the internet forever. Prevention tốn 5 phút. Recovery tốn 5 giờ. Breach tốn millions."

  1. Sign your commits: Protect identity, enable "Verified" badge
  2. SSH signing is easier: Reuse existing SSH keys, simpler setup
  3. Rotate FIRST, clean SECOND: Khi leak xảy ra, rotate credentials ngay
  4. filter-repo > filter-branch: Modern, faster, easier
  5. Prevention is mandatory: Global gitignore + pre-commit hooks + scanning tools

The Security Stack

┌─────────────────────────────────────────┐
│  GitHub Secret Scanning (Cloud)         │ ← Last resort alert
├─────────────────────────────────────────┤
│  CI/CD: Trufflehog scan                 │ ← Pre-merge check
├─────────────────────────────────────────┤
│  Pre-commit: git-secrets hook           │ ← Before commit
├─────────────────────────────────────────┤
│  Global .gitignore                      │ ← Never stage
├─────────────────────────────────────────┤
│  Environment variables / Vault          │ ← Don't hardcode
└─────────────────────────────────────────┘

SECURITY IS LAYERS

Không tool nào 100% effective. Dùng multiple layers để maximize protection.

🧠 Quiz

Câu 1: Khi phát hiện đã commit secret (API key) vào repository, bước đầu tiên phải làm gì?

  • [ ] A) Dùng git filter-branch xóa commit chứa secret
  • [ ] B) Tạo commit mới xóa file chứa secret
  • [x] C) Rotate (đổi mới) credentials ngay lập tức, vì secret có thể đã bị expose
  • [ ] D) Đổi tên file chứa secret

💡 Giải thích: "Rotate FIRST, clean SECOND." Ngay khi secret bị commit, coi như nó đã public (có thể đã được cache, clone, mirror). Đổi credentials ngay, sau đó mới dọn dẹp lịch sử Git.

Câu 2: Tại sao nên ký commit bằng GPG/SSH key?

  • [ ] A) Để commit nhanh hơn
  • [ ] B) Để encrypt nội dung commit
  • [x] C) Để xác minh danh tính người commit, hiển thị badge "Verified" và ngăn impersonation
  • [ ] D) Để tự động backup commit lên cloud

💡 Giải thích: Git cho phép ai cũng set tên/email bất kỳ trong git config. Signing commit với GPG/SSH chứng minh commit thực sự từ bạn, hiển thị "Verified" badge trên GitHub, ngăn ngừa identity spoofing.

Câu 3: "Defense in Depth" trong Git security nghĩa là gì?

  • [ ] A) Chỉ cần một tool scanning mạnh là đủ
  • [ ] B) Encrypt toàn bộ repository
  • [x] C) Sử dụng nhiều lớp bảo vệ: gitignore → pre-commit hooks → CI scanning → cloud scanning
  • [ ] D) Giới hạn quyền push chỉ cho team lead

💡 Giải thích: Không tool nào 100% effective. Kiến trúc bảo mật nhiều lớp: environment variables (don't hardcode) → global .gitignore → pre-commit hooks (git-secrets) → CI/CD scanning (Trufflehog) → GitHub Secret Scanning.