Git secrets: prevenindo commit de credenciais acidentalmente
1. O Problema: Credenciais no Histórico do Git
1.1. Cenários comuns de vazamento
O vazamento de credenciais em repositórios Git é um dos incidentes de segurança mais frequentes e perigosos no desenvolvimento de software. Os cenários típicos incluem:
- Um desenvolvedor faz commit de um arquivo
.envcontendo senhas de banco de dados - Chaves SSH privadas (
id_rsa,*.pem) são incluídas acidentalmente em um commit - Tokens de API da AWS, GitHub ou Stripe aparecem em arquivos de configuração
- Strings de conexão com bancos de produção são hardcoded no código-fonte
1.2. Consequências irreversíveis mesmo após remover o commit
Muitos desenvolvedores acreditam que, ao remover um commit com git reset ou git revert, o problema está resolvido. Isso é uma ilusão perigosa. O Git mantém todo o histórico de commits no repositório local e remoto. Mesmo após um git push --force, os commits antigos permanecem acessíveis via git reflog por pelo menos 90 dias no GitHub e GitLab.
Uma credencial commitada acidentalmente pode:
- Ser descoberta por ferramentas de varredura automática (como GitHub Secret Scanning)
- Permitir acesso não autorizado a serviços críticos
- Gerar custos financeiros significativos (ex: uso não autorizado de serviços cloud)
- Expor dados de clientes e violar regulamentações como LGPD ou GDPR
1.3. Como o Git trata arquivos rastreados vs. não rastreados
A diferença entre arquivos rastreados (tracked) e não rastreados (untracked) é crucial para entender a prevenção de vazamentos:
- Arquivos não rastreados: arquivos que nunca foram adicionados ao staging. O
.gitignorefunciona perfeitamente aqui. - Arquivos rastreados: arquivos que já estão no índice do Git. Uma vez rastreados, o
.gitignorenão tem efeito sobre eles.
# Verificando se um arquivo está rastreado
git ls-files --error-unmatch config/credentials.json
# Se retornar o nome do arquivo, ele está rastreado
# Se retornar erro, não está rastreado
2. .gitignore: Primeira Linha de Defesa
2.1. Padrões globais e locais para excluir arquivos sensíveis
O .gitignore é a ferramenta mais básica e essencial. Crie um arquivo .gitignore na raiz do repositório com padrões específicos:
# Arquivos de ambiente
.env
.env.local
.env.production
# Chaves e certificados
*.pem
*.key
*.cert
id_rsa
id_rsa.pub
# Arquivos de configuração com credenciais
credentials.json
config/database.yml
secrets.yml
# Diretórios comuns de configuração
.aws/
.gcp/
.ssh/
2.2. Cuidados com .gitignore após o primeiro commit
Se um arquivo sensível já foi commitado, adicioná-lo ao .gitignore não resolve o problema. O Git continuará rastreando as alterações desse arquivo. Para corrigir:
# Remover do índice sem deletar o arquivo local
git rm --cached config/credentials.json
# Agora sim, o .gitignore funcionará para esse arquivo
echo "config/credentials.json" >> .gitignore
git add .gitignore
git commit -m "Remove credentials.json do rastreamento"
2.3. Exemplos práticos
# .gitignore completo para projetos Node.js com credenciais
node_modules/
dist/
.env
.env.*
*.log
npm-debug.log*
.DS_Store
coverage/
.nyc_output/
*.pem
*.key
secrets/
config/production.json
3. Git Hooks Client-Side para Bloqueio Automático
3.1. Pre-commit hook: escaneando arquivos antes do commit
Git hooks são scripts que executam automaticamente em eventos específicos. O pre-commit hook roda antes do commit ser criado, permitindo bloquear commits que contenham padrões suspeitos.
3.2. Implementação de um hook simples com grep e exit 1
Crie o arquivo .git/hooks/pre-commit:
#!/bin/sh
# Padrões de credenciais a serem verificados
PATTERNS="password=|senha=|api_key=|secret=|token=|aws_secret|-----BEGIN RSA PRIVATE KEY-----"
echo "🔍 Verificando credenciais nos arquivos modificados..."
# Lista arquivos staged (excluindo deletados)
FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$FILES" ]; then
exit 0
fi
# Verifica cada arquivo
for FILE in $FILES; do
if [ -f "$FILE" ]; then
if grep -qE "$PATTERNS" "$FILE"; then
echo "❌ ERRO: Credencial detectada em $FILE"
echo " Remova a credencial antes de commitar."
exit 1
fi
fi
done
echo "✅ Nenhuma credencial encontrada. Commit permitido."
exit 0
Torne o hook executável:
chmod +x .git/hooks/pre-commit
3.3. Compartilhando hooks com a equipe via core.hooksPath
Para compartilhar hooks com toda a equipe, armazene-os em um diretório versionado:
# Estrutura do repositório
meu-projeto/
├── .githooks/
│ └── pre-commit
├── .gitignore
└── src/
# Configure cada desenvolvedor para usar esse diretório
git config core.hooksPath .githooks
Adicione essa configuração ao README.md ou a um script de setup automático.
4. Git Secrets: Ferramenta Open Source da AWS Labs
4.1. Instalação e configuração básica
Git Secrets é uma ferramenta desenvolvida pela AWS Labs que automatiza a detecção de credenciais:
# Instalação no macOS (Homebrew)
brew install git-secrets
# Instalação no Linux
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install
# Configuração inicial
git secrets --install
git secrets --register-aws
4.2. Definindo padrões de busca (regex) e arquivos permitidos
# Adicionar padrões personalizados
git secrets --add 'senha\s*=\s*["\x27]?[A-Za-z0-9!@#$%^&*()_+=-]{8,}["\x27]?'
git secrets --add 'api_key\s*[:=]\s*["\x27][A-Za-z0-9]{20,}["\x27]'
# Adicionar provedores (AWS, GCP, etc.)
git secrets --add-provider -- cat ~/.git-secrets-providers/aws.txt
# Permitir arquivos específicos (ex: testes que usam tokens mock)
git secrets --add --allowed 'test_token_12345'
4.3. Integração com pre-commit hook para execução automática
# O comando git secrets --install já cria o hook automaticamente
# Verifique se está funcionando:
cat .git/hooks/pre-commit
# Saída esperada:
#!/bin/sh
git secrets --pre_commit_hook -- "$@"
Teste a configuração:
# Criar um arquivo de teste com credencial falsa
echo "password=super_secreta_123" > test_creds.txt
git add test_creds.txt
git commit -m "teste" # Deve falhar
5. Remediation: Removendo Credenciais do Histórico
5.1. Usando git filter-branch para reescrever o histórico
Se uma credencial já foi commitada, use git filter-branch:
# Remover um arquivo específico de todo o histórico
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch config/credentials.json" \
--prune-empty --tag-name-filter cat -- --all
# Substituir texto sensível em todo o histórico
git filter-branch --force --tree-filter \
"sed -i 's/senha_real_123/REMOVED/g' *.env" \
--prune-empty --tag-name-filter cat -- --all
5.2. Alternativa moderna: git filter-repo (BFG Repo-Cleaner)
O git filter-repo é mais rápido e seguro:
# Instalação
pip install git-filter-repo
# Remover arquivo específico
git filter-repo --path config/credentials.json --invert-paths
# Substituir texto em todo o histórico
git filter-repo --replace-text <(echo "senha_real_123==>REMOVED")
# Remover arquivos por padrão
git filter-repo --path-glob '*.pem' --invert-paths
5.3. Impacto em repositórios compartilhados e git push --force
Após reescrever o histórico, todos os colaboradores precisam:
# No repositório local de cada desenvolvedor
git fetch --all
git reset --hard origin/main
# Forçar push para o remoto (com cuidado!)
git push --force --all
git push --force --tags
ATENÇÃO: Notifique toda a equipe antes de forçar o push. Idealmente, bloqueie o repositório durante a operação.
6. Políticas Server-Side com Pre-Receive Hooks
6.1. Validando novos commits no servidor remoto
No GitHub, GitLab ou servidores auto-hospedados, configure hooks server-side para rejeitar commits com credenciais antes que cheguem ao repositório central.
6.2. Script de rejeição para padrões de credenciais
Exemplo de pre-receive hook para servidor Git:
#!/bin/bash
# pre-receive hook para rejeitar credenciais
zero_commit="0000000000000000000000000000000000000000"
while read oldrev newrev refname; do
# Lista todos os commits novos
for commit in $(git rev-list $oldrev..$newrev); do
# Verifica diff do commit
if git diff-tree --no-commit-id -r $commit | grep -qE '(password|secret|token|key)\s*[:=]'; then
echo "❌ REJEITADO: Credencial detectada no commit $commit"
echo " Remova a credencial e faça um novo commit."
exit 1
fi
done
done
exit 0
6.3. Mensagens de erro amigáveis e instruções para o desenvolvedor
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ ❌ COMMIT REJEITADO: Credenciais detectadas ║"
echo "║ ║"
echo "║ Seu commit contém padrões que parecem ser credenciais. ║"
echo "║ ║"
echo "║ Para corrigir: ║"
echo "║ 1. Use git revert no último commit ║"
echo "║ 2. Remova as credenciais dos arquivos ║"
echo "║ 3. Adicione os arquivos ao .gitignore ║"
echo "║ 4. Faça um novo commit ║"
echo "║ ║"
echo "║ Dica: Use 'git secrets' para verificar localmente ║"
echo "╚══════════════════════════════════════════════════════════════╝"
7. Boas Práticas e Automação Contínua
7.1. Template de repositório com hooks e .gitignore pré-configurados
Crie um template de repositório no GitHub/GitLab com:
meu-template/
├── .githooks/
│ └── pre-commit # Script de verificação
├── .gitignore # Com padrões de segurança
├── setup.sh # Script de configuração automática
└── README.md
7.2. Integração com CI/CD para escanear branches antes do merge
No GitHub Actions, adicione um workflow:
name: Secret Scanning
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install git-secrets
run: |
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets && sudo make install
- name: Run git-secrets
run: |
git secrets --scan-history
7.3. Cultura de equipe: checklist de segurança para commits
Checklist para cada desenvolvedor antes do commit:
- [ ] Revisei os arquivos staged (
git diff --cached) - [ ] Não há senhas, tokens ou chaves nos arquivos
- [ ] Arquivos com credenciais estão no
.gitignore - [ ] Executei
git secrets --scan(se instalado) - [ ] Testei o pre-commit hook localmente
Referências
- Git Documentation - Git Hooks — Documentação oficial sobre hooks client-side e server-side no Git.
- AWS Labs - git-secrets — Repositório oficial da ferramenta open source para prevenção de commit de credenciais.
- GitHub - Removing sensitive data from a repository — Guia oficial do GitHub para remover dados sensíveis do histórico.
- GitLab - Secret Detection — Documentação sobre detecção automática de segredos no GitLab.
- BFG Repo-Cleaner — Ferramenta alternativa e mais rápida que git filter-branch para remover arquivos do histórico Git.
- Atlassian - Git .gitignore — Tutorial completo sobre o uso do .gitignore para excluir arquivos sensíveis.
- OWASP - Credential Stuffing Prevention — Guia de prevenção de vazamento de credenciais, aplicável a práticas de Git.