Advanced

Git Security

Comprehensive guide to Git security best practices, threat mitigation, and secure development workflows

Git Security: Protecting Your Code and Development Workflow

Git security encompasses protecting your source code, development workflows, and infrastructure from threats. This comprehensive guide covers security best practices, threat mitigation strategies, and secure collaboration patterns.

Understanding Git Security Threats

Common Security Risks

Threat CategoryRisksImpact
Code ExposureSecrets in commits, public repositoriesData breaches, unauthorized access
Supply ChainMalicious dependencies, compromised reposCode injection, backdoors
AuthenticationWeak credentials, stolen tokensUnauthorized code changes
Access ControlOver-privileged users, poor permissionsData leakage, malicious changes
History TamperingForce pushes, rewritten historyLoss of audit trail
Social EngineeringPhishing, impersonationAccount compromise

Attack Vectors

# Common attack scenarios:

# 1. Secrets in repository history
git log --all --full-history -- "*password*" "*key*" "*token*"

# 2. Malicious commits disguised as legitimate changes
git log --oneline --author="legitimate-dev@company.com"

# 3. Branch protection bypass
git push --force-with-lease origin main

# 4. Submodule poisoning
git submodule status  # Check for unexpected submodules

# 5. Tag/release tampering
git verify-tag v1.0.0  # Verify signed tags

Authentication and Access Control

SSH Key Security

Generate Secure SSH Keys

# Use Ed25519 (recommended) or RSA with sufficient key size
ssh-keygen -t ed25519 -C "your.email@example.com" -f ~/.ssh/github_ed25519

# For RSA (if Ed25519 not supported)
ssh-keygen -t rsa -b 4096 -C "your.email@example.com" -f ~/.ssh/github_rsa

# Set secure permissions
chmod 600 ~/.ssh/github_ed25519
chmod 644 ~/.ssh/github_ed25519.pub

# Use strong passphrase
ssh-keygen -p -f ~/.ssh/github_ed25519

SSH Key Management

# Separate keys for different services/purposes
ssh-keygen -t ed25519 -f ~/.ssh/github_work
ssh-keygen -t ed25519 -f ~/.ssh/github_personal
ssh-keygen -t ed25519 -f ~/.ssh/gitlab_company

# Configure SSH client (~/.ssh/config)
Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_work
    IdentitiesOnly yes

Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_personal
    IdentitiesOnly yes

# Use specific SSH config for repositories
git remote set-url origin git@github-work:company/repo.git

SSH Agent Security

# Use SSH agent with timeout
ssh-add -t 3600 ~/.ssh/github_ed25519  # 1 hour timeout

# List loaded keys
ssh-add -l

# Remove all keys from agent
ssh-add -D

# Require confirmation for key usage
ssh-add -c ~/.ssh/github_ed25519

# Use hardware security keys (if supported)
ssh-keygen -t ed25519-sk -C "your.email@example.com"

Personal Access Tokens

# Create fine-grained personal access tokens
# GitHub: Settings → Developer settings → Personal access tokens → Fine-grained tokens

# Configure token with minimal required permissions:
# - Repository permissions: Contents (read), Metadata (read), Pull requests (write)
# - Account permissions: None (unless specifically needed)

# Store token securely
git config --global credential.helper store  # Not recommended for production
git config --global credential.helper cache --timeout=3600  # Temporary cache

# Use credential managers (recommended)
# Windows: Git Credential Manager
# macOS: Keychain Access
# Linux: libsecret or pass

# Environment variable approach (CI/CD)
export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
git clone https://$GITHUB_TOKEN@github.com/user/repo.git

Multi-Factor Authentication

# Enable MFA on all Git hosting platforms
# GitHub: Settings → Account security → Two-factor authentication

# Use TOTP apps (recommended over SMS)
# - Google Authenticator
# - Authy
# - 1Password
# - Bitwarden

# Hardware security keys (highest security)
# - YubiKey
# - Google Titan Security Key
# - Solo keys

# Recovery codes
# Store securely offline
# Test recovery process regularly

Secrets Management

Detecting Secrets in Repository

Automated Secret Scanning

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

# Configure git-secrets for repository
git secrets --register-aws
git secrets --install

# Add custom patterns
git secrets --add 'password\s*=\s*.+'
git secrets --add 'api[_-]?key\s*=\s*.+'
git secrets --add --allowed 'password = <PASSWORD>'  # Allow placeholder

# Scan repository
git secrets --scan
git secrets --scan-history

# Pre-commit hook integration
echo "git secrets --pre_commit_hook -- \"\$@\"" > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

Alternative Secret Detection Tools

# TruffleHog for comprehensive secret detection
pip install truffleHog
truffleHog --regex --entropy=False https://github.com/user/repo.git

# GitLeaks
git clone https://github.com/zricethezav/gitleaks.git
cd gitleaks && make build
./gitleaks detect --source . --report-path report.json

# Detect-secrets by Yelp
pip install detect-secrets
detect-secrets scan --all-files .
detect-secrets audit .secrets.baseline

Removing Secrets from History

# Install git-filter-repo
pip install git-filter-repo

# Remove file containing secrets
git filter-repo --path secrets.txt --invert-paths

# Remove secrets by content pattern
git filter-repo --replace-text expressions.txt

# expressions.txt content:
# password=.*==>password=REDACTED
# api_key=.*==>api_key=REDACTED
# SECRET_TOKEN=.*==>SECRET_TOKEN=REDACTED

# Remove specific commits
git filter-repo --commit-callback '
  if commit.message == b"Add API keys":
    commit.skip()
'

Using BFG Repo Cleaner

# Download BFG
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar

# Remove passwords from all files
java -jar bfg-1.14.0.jar --replace-text passwords.txt repo.git

# Remove files containing secrets
java -jar bfg-1.14.0.jar --delete-files "*.key" repo.git
java -jar bfg-1.14.0.jar --delete-files "secrets.txt" repo.git

# Clean up
cd repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive

Secure Secret Storage

Environment Variables

# Store secrets as environment variables
export DATABASE_PASSWORD="secure_password_here"
export API_KEY="secret_api_key_here"

# Use in application code, not Git repository
# JavaScript example:
# const dbPassword = process.env.DATABASE_PASSWORD;

# Create template files for secrets
# .env.template:
DATABASE_PASSWORD=your_database_password_here
API_KEY=your_api_key_here

# Add .env to .gitignore
echo ".env" >> .gitignore
echo "*.local" >> .gitignore
echo "config/secrets.yml" >> .gitignore

External Secret Management

# AWS Secrets Manager example
aws secretsmanager get-secret-value --secret-id prod/database/password

# HashiCorp Vault example
vault kv get -field=password secret/database

# Azure Key Vault example
az keyvault secret show --name database-password --vault-name MyKeyVault

# Use secret management tools in CI/CD
# GitHub Actions example:
# - name: Get secrets
#   env:
#     DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}

Repository Security Configuration

Branch Protection

# Configure branch protection via GitHub API
curl -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  https://api.github.com/repos/owner/repo/branches/main/protection \
  -d '{
    "required_status_checks": {
      "strict": true,
      "contexts": ["ci/tests", "ci/security-scan"]
    },
    "enforce_admins": true,
    "required_pull_request_reviews": {
      "required_approving_review_count": 2,
      "dismiss_stale_reviews": true,
      "require_code_owner_reviews": true,
      "restrict_review_dismissals": {
        "users": ["security-team"],
        "teams": ["security"]
      }
    },
    "restrictions": {
      "users": ["admin-user"],
      "teams": ["core-team"]
    },
    "required_linear_history": true,
    "allow_force_pushes": false,
    "allow_deletions": false
  }'

Repository Security Settings

# Enable security features via GitHub settings or API

# Vulnerability alerts
curl -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/repo/vulnerability-alerts

# Dependency graph
curl -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/repo/dependency-graph

# Security advisories
curl -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/repo/security-advisories

# Code scanning
# Add to .github/workflows/codeql-analysis.yml

Access Control Configuration

# Principle of least privilege
# Repository access levels:
# - Read: Clone, pull, download
# - Triage: Read + manage issues/PRs (no code changes)
# - Write: Read + push to non-protected branches
# - Maintain: Write + manage repository settings
# - Admin: Full access

# Team-based access control
# Create teams for different roles:
# - developers (Write access)
# - reviewers (Write access, required for PR approvals)
# - security-team (Admin access)
# - ci-bots (Write access, limited to specific branches)

# Regular access review
# Audit repository access quarterly
# Remove inactive users
# Review bot/service account permissions

Secure Development Workflows

Commit Signing

GPG Signing Setup

# Generate GPG key
gpg --full-generate-key
# Choose RSA and RSA, 4096 bits, expires in 1 year

# List GPG keys
gpg --list-secret-keys --keyid-format LONG

# Configure Git to use GPG
git config --global user.signingkey YOUR_GPG_KEY_ID
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# Export public key for GitHub
gpg --armor --export YOUR_GPG_KEY_ID

# Sign commits and tags
git commit -S -m "Signed commit"
git tag -s v1.0.0 -m "Signed release"

# Verify signatures
git log --show-signature
git verify-tag v1.0.0

SSH Commit Signing (Git 2.34+)

# Configure SSH signing
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/github_ed25519.pub
git config --global commit.gpgsign true

# Create allowed signers file for verification
echo "$(git config user.email) $(cat ~/.ssh/github_ed25519.pub)" > ~/.ssh/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

# Verify SSH signatures
git log --show-signature

Secure Code Review Process

# Pre-review security checks
# 1. Automated security scanning
# 2. Dependency vulnerability check
# 3. Secret detection
# 4. License compliance

# Security-focused review checklist:
# - Input validation and sanitization
# - Authentication and authorization
# - Encryption and data protection
# - Error handling and logging
# - Dependency updates and vulnerabilities
# - Configuration security

CODEOWNERS for Security Review

# .github/CODEOWNERS file
# Global rules
* @security-team

# Specific patterns requiring security review
*.sql @database-team @security-team
Dockerfile* @platform-team @security-team
*.yml @devops-team @security-team
*.yaml @devops-team @security-team

# Critical paths
/src/auth/ @security-team @auth-team
/src/payment/ @security-team @payment-team
/.github/ @security-team @devops-team

# Configuration files
config/ @security-team
secrets/ @security-team

Supply Chain Security

Dependency Security

Dependency Scanning

# Node.js dependencies
npm audit
npm audit fix

# Python dependencies
pip-audit
safety check

# Ruby dependencies
bundle audit
bundle audit update

# Go dependencies
go list -json -m all | nancy sleuth

# Multi-language scanning with Snyk
npm install -g snyk
snyk auth
snyk test
snyk monitor

Dependency Pinning

# Package.json - use exact versions
{
  "dependencies": {
    "express": "4.18.2",
    "lodash": "4.17.21"
  }
}

# Use lock files
# - package-lock.json (npm)
# - yarn.lock (Yarn)
# - Pipfile.lock (Python)
# - Gemfile.lock (Ruby)
# - go.sum (Go)

# Regularly update dependencies
npm update
npm audit fix

Submodule Security

# Verify submodule URLs before adding
git submodule add https://github.com/trusted-org/library.git vendor/library

# Pin submodules to specific commits
cd vendor/library
git checkout v1.2.3
cd ../..
git add vendor/library
git commit -m "Pin library to v1.2.3"

# Audit existing submodules
git submodule foreach git log --oneline -10
git submodule foreach git remote show origin

# Update submodules securely
git submodule update --remote
git submodule foreach 'git checkout $(git describe --tags --abbrev=0)'

Container Security

# Dockerfile security best practices
# Use specific base image versions
FROM node:16.19.0-alpine

# Don't run as root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs

# Multi-stage builds to reduce attack surface
FROM node:16.19.0-alpine AS builder
COPY package*.json ./
RUN npm ci --only=production

FROM node:16.19.0-alpine AS runtime
COPY --from=builder /app/node_modules ./node_modules
COPY . .
USER 1001

Incident Response and Forensics

Security Incident Detection

# Monitor for suspicious activities
# 1. Unexpected force pushes
git reflog --all | grep "forced-update"

# 2. Commits from unexpected authors
git log --all --author=".*admin.*" --oneline
git log --all --since="1 week ago" --format="%h %an %ae %s"

# 3. Large commits or binary additions
git log --all --stat | grep "Bin"
git rev-list --objects --all | git cat-file --batch-check="%(objecttype) %(objectname) %(objectsize) %(rest)" | awk '/^blob/ && $3 > 1048576'

# 4. Suspicious file additions
git log --all --name-status | grep -E "\.(exe|bat|sh|ps1)$"

# 5. Branch/tag manipulations
git for-each-ref --format="%(refname) %(objectname) %(authordate)" refs/

Forensic Analysis

# Create forensic copy of repository
git clone --mirror suspicious-repo.git forensic-copy.git

# Analyze commit history
git log --all --graph --pretty=format:"%h %an %ae %ad %s" --date=iso
git log --all --stat --since="2023-01-01" --until="2023-12-31"

# Check for history rewriting
git reflog --all
git fsck --full --strict

# Examine specific commits
git show --stat SUSPICIOUS_COMMIT
git diff SUSPICIOUS_COMMIT^..SUSPICIOUS_COMMIT

# Check file permissions and executable changes
git log --all -p | grep -E "^\+.*chmod|^\+.*executable"

Recovery Procedures

# Immediate response steps:

# 1. Revoke compromised credentials
# - Rotate API keys
# - Regenerate SSH keys
# - Invalidate personal access tokens

# 2. Assess damage scope
git log --all --since="DATE_OF_COMPROMISE" --format="%h %an %ae %s"

# 3. Restore from clean backup
git clone clean-backup.git restored-repo
cd restored-repo

# 4. Cherry-pick legitimate commits
git cherry-pick LEGITIMATE_COMMIT_HASH

# 5. Force push to reset remote (if necessary)
git push --force-with-lease origin main

# 6. Audit and strengthen security
# - Review access controls
# - Update branch protection rules
# - Implement additional monitoring

Security Monitoring and Auditing

Automated Security Monitoring

GitHub Actions Security Workflow

# .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * 0'  # Weekly scan

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # Full history for secret scanning

      - name: Run secret detection
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: main
          head: HEAD

      - name: Dependency vulnerability scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

      - name: Container security scan
        uses: anchore/scan-action@v3
        with:
          image: "myapp:latest"
          severity-cutoff: high

      - name: Code security analysis
        uses: github/codeql-action/analyze@v2
        with:
          languages: javascript, python

Security Metrics and Reporting

#!/bin/bash
# Security audit script

echo "=== Git Security Audit Report ==="
echo "Generated: $(date)"
echo

# Check for unsigned commits
echo "Unsigned commits in last 30 days:"
git log --since="30 days ago" --pretty=format:"%h %an %s %G?" | grep -v "G"

# Check for force pushes
echo "Force pushes detected:"
git reflog --all | grep "forced-update" | tail -10

# Check for large binary files
echo "Large files in repository:"
git rev-list --objects --all | 
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
awk '/^blob/ && $3 > 1048576 { print $4, $3/1024/1024 "MB" }' |
sort -k2 -nr | head -10

# Check recent committers
echo "Recent committers:"
git shortlog -sne --since="7 days ago"

# Check branch protection status
echo "Protected branches:"
gh api repos/:owner/:repo/branches --jq '.[] | select(.protected == true) | .name'

Compliance and Governance

Regulatory Compliance

GDPR Compliance

# Data protection considerations for Git repositories:

# 1. Avoid storing personal data in Git history
# - Email addresses (use generic ones for commits)
# - User IDs, names in test data
# - Personal identifiable information

# 2. Right to be forgotten implementation
# - Ability to remove user data from history
# - Use git-filter-repo for complete removal
# - Document data retention policies

# 3. Data processing records
# - Log all repository access
# - Maintain audit trail of changes
# - Document purposes for data processing

SOC 2 / ISO 27001 Compliance

# Security controls implementation:

# Access controls
# - Multi-factor authentication required
# - Role-based access control
# - Regular access reviews

# Change management
# - All changes via pull requests
# - Mandatory code reviews
# - Signed commits for traceability

# Monitoring and logging
# - All Git operations logged
# - Automated security scanning
# - Incident response procedures

Policy Enforcement

Git Hooks for Policy Enforcement

#!/bin/sh
# .githooks/pre-commit - Policy enforcement hook

# Check commit message format
commit_message_regex="^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}$"
commit_message=$(cat "$1")

if ! echo "$commit_message" | grep -qE "$commit_message_regex"; then
    echo "Error: Commit message doesn't follow company policy"
    echo "Format: type(scope): description"
    exit 1
fi

# Check for secrets
if git diff --cached --name-only | xargs grep -l "password\|api_key\|secret" 2>/dev/null; then
    echo "Error: Potential secrets detected in commit"
    exit 1
fi

# Check file size limits
max_file_size=10485760  # 10MB
while read -r file; do
    file_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
    if [ "$file_size" -gt "$max_file_size" ]; then
        echo "Error: File $file exceeds size limit (${file_size} > ${max_file_size})"
        exit 1
    fi
done < <(git diff --cached --name-only)

exit 0

Security Testing and Validation

Security Test Automation

#!/bin/bash
# Security test suite

echo "Running security validation tests..."

# Test 1: Check for secrets in repository
echo "1. Testing for secrets..."
if git secrets --scan 2>/dev/null; then
    echo "✓ No secrets detected"
else
    echo "✗ Secrets found in repository"
    exit 1
fi

# Test 2: Verify all commits are signed
echo "2. Verifying commit signatures..."
unsigned_commits=$(git log --format="%h %G?" | grep -v "G" | wc -l)
if [ "$unsigned_commits" -eq 0 ]; then
    echo "✓ All commits are signed"
else
    echo "✗ $unsigned_commits unsigned commits found"
fi

# Test 3: Check for vulnerable dependencies
echo "3. Checking dependencies for vulnerabilities..."
if command -v npm audit >/dev/null 2>&1; then
    if npm audit --audit-level high; then
        echo "✓ No high-severity vulnerabilities"
    else
        echo "✗ High-severity vulnerabilities found"
        exit 1
    fi
fi

# Test 4: Verify branch protection
echo "4. Checking branch protection..."
if gh api repos/:owner/:repo/branches/main --jq '.protected'; then
    echo "✓ Main branch is protected"
else
    echo "✗ Main branch is not protected"
fi

echo "Security validation completed"

Penetration Testing

# Git-specific penetration testing scenarios:

# 1. Test SSH key security
ssh-keygen -l -f ~/.ssh/id_rsa.pub  # Check key strength
ssh-add -L | ssh-keygen -l -f -     # Check loaded keys

# 2. Test commit verification bypass
git commit --no-verify -m "Bypass attempt"

# 3. Test branch protection bypass
git push --force-with-lease origin main

# 4. Test secret detection evasion
echo "password=admin" | base64  # Encoded secrets
echo "passwd" + "word=admin"    # Split strings

# 5. Test access control
git ls-remote origin            # Test read access
git push origin test-branch     # Test write access

Best Practices Summary

Security Configuration Checklist

# Essential security configurations
git config --global user.signingkey YOUR_GPG_KEY_ID
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# Repository security
git secrets --install
git config --bool branch.main.rebase true

# SSH security
# - Use Ed25519 keys
# - Separate keys per service
# - Enable SSH agent timeout
# - Use hardware security keys when possible

Security Do's and Don'ts

Do:

  • Sign all commits and tags
  • Use strong, unique SSH keys
  • Enable multi-factor authentication
  • Regularly scan for secrets and vulnerabilities
  • Implement branch protection rules
  • Monitor repository activity
  • Maintain incident response procedures
  • Keep dependencies updated

Don't:

  • Store secrets in repository history
  • Use shared or weak SSH keys
  • Skip security reviews for critical changes
  • Ignore vulnerability alerts
  • Allow force pushes to protected branches
  • Disable security features for convenience
  • Trust external dependencies without verification
  • Forget to revoke access for departed team members

Next Steps

🛡️ Congratulations! You now have comprehensive Git security knowledge.

Enhance your security posture:

  1. Implement Git Hooks - Automate security checks
  2. Optimize Performance - Secure and fast repositories
  3. Design Team Workflows - Secure collaborative development

Advanced Security Topics

  • Implement zero-trust Git workflows
  • Design secure CI/CD pipelines with Git integration
  • Create automated security compliance reporting
  • Develop custom security tools using Git internals
  • Implement distributed code signing workflows
  • Design incident response playbooks for Git security