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.