Como conduzir uma revisão de segurança de código em pull requests
1. Fundamentos da Revisão de Segurança em PRs
1.1. Diferença entre revisão funcional e revisão de segurança
A revisão funcional verifica se o código atende aos requisitos de negócio, enquanto a revisão de segurança foca em identificar vulnerabilidades que possam comprometer a confidencialidade, integridade ou disponibilidade do sistema. Enquanto a primeira pergunta "o código funciona?", a segunda pergunta "o código pode ser explorado?".
1.2. O papel do desenvolvedor como primeira linha de defesa
Desenvolvedores são a primeira barreira contra vulnerabilidades. Cada PR representa uma oportunidade de prevenir falhas antes que cheguem à produção. A mentalidade de segurança deve ser incorporada desde a escrita do código, não apenas na revisão final.
1.3. Checklist mínimo de segurança para todo PR
Checklist de Segurança para PR:
□ Dados de entrada são validados e sanitizados?
□ Consultas ao banco usam parâmetros (não concatenação)?
□ Senhas e tokens não estão hardcoded?
□ Novas dependências são de fontes confiáveis?
□ Logs não expõem dados sensíveis?
□ Permissões foram verificadas para novos endpoints?
□ Variáveis de ambiente não têm valores sensíveis hardcoded?
2. Identificação de Vulnerabilidades Comuns em Pull Requests
2.1. Injeção de SQL, comandos e XSS em alterações de entrada/saída
Exemplo de SQL Injection (ERRADO):
// Código vulnerável
const query = `SELECT * FROM users WHERE username = '${req.body.username}'`;
db.execute(query);
Exemplo corrigido:
// Código seguro com parâmetros
const query = 'SELECT * FROM users WHERE username = ?';
db.execute(query, [req.body.username]);
Para XSS, verifique se a saída está escapada:
// Em templates (exemplo com React)
// ERRADO: <div>{userInput}</div>
// CORRETO: <div>{escapeHtml(userInput)}</div>
2.2. Exposição acidental de segredos, tokens e chaves de API
Procure por padrões suspeitos no diff:
// Padrões a serem verificados no PR
- "api_key", "apikey", "secret", "password", "token"
- Strings com aparência de base64, JWT ou hashes
- Comentários com credenciais de teste
- Arquivos .env commitados acidentalmente
2.3. Uso inseguro de bibliotecas e dependências adicionadas no PR
Verifique se novas dependências:
- São de mantenedores conhecidos
- Têm versão específica (não latest)
- Não possuem vulnerabilidades conhecidas (CVE)
- São necessárias ou podem ser substituídas por funcionalidades nativas
3. Análise de Fluxos de Dados e Controle de Acesso
3.1. Verificação de autenticação e autorização em novas rotas/endpoints
Exemplo de rota sem autenticação (ERRADO):
// app.js
app.get('/api/admin/users', getUsers); // Sem middleware de autenticação!
Exemplo corrigido:
// app.js
app.get('/api/admin/users', authenticate, authorize('admin'), getUsers);
3.2. Validação de permissões em alterações de lógica de negócio
Em PRs que alteram lógica de negócio, verifique:
// Verificar se o usuário só acessa seus próprios recursos
// ERRADO: const order = await Order.findById(orderId);
// CORRETO: const order = await Order.findOne({ _id: orderId, userId: req.user.id });
3.3. Rastreamento de dados sensíveis em logs e respostas da API
Exemplo de log expondo dados sensíveis:
// ERRADO
console.log('Usuário logado:', user);
// Pode expor: { _id: ..., email: 'user@email.com', passwordHash: '...', cpf: '...' }
// CORRETO
console.log('Usuário logado:', { userId: user._id, email: user.email });
4. Revisão de Configurações e Infraestrutura como Código
4.1. Análise de arquivos Dockerfile, docker-compose e Kubernetes manifests
Verifique no Dockerfile:
# ERRADO: expor portas desnecessárias
EXPOSE 22 3306 6379
# CORRETO: expor apenas a porta da aplicação
EXPOSE 3000
4.2. Verificação de permissões de rede e exposição de portas desnecessárias
No docker-compose:
# ERRADO: expondo banco de dados para o mundo
services:
db:
ports:
- "0.0.0.0:3306:3306"
# CORRETO: apenas rede interna
db:
expose:
- "3306"
4.3. Configuração de variáveis de ambiente e defaults inseguros
# ERRADO: default com valor sensível
DATABASE_URL=postgres://admin:admin123@localhost:5432/db
# CORRETO: sem default ou com placeholder
DATABASE_URL=${DATABASE_URL}
5. Ferramentas Automatizadas e Integração Contínua
5.1. Configuração de SAST (Static Application Security Testing) no pipeline
Exemplo de configuração para GitHub Actions:
# .github/workflows/security.yml
name: Security Scan
on: [pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
5.2. Uso de linters de segurança como Bandit, Semgrep ou ESLint plugins
Para Python com Bandit:
# Comando no pipeline
bandit -r src/ -f json -o bandit-report.json
Para JavaScript com ESLint security plugin:
# .eslintrc.json
{
"plugins": ["security"],
"extends": ["plugin:security/recommended"]
}
5.3. Integração de scanners de dependências (Dependabot, Snyk, Trivy)
Exemplo com Dependabot:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
6. Técnicas de Revisão Manual e Análise de Contexto
6.1. Leitura orientada a padrões de ataque (OWASP Top 10)
Ao revisar, mantenha em mente:
- A01: Broken Access Control
- A03: Injection
- A05: Security Misconfiguration
- A06: Vulnerable and Outdated Components
6.2. Revisão de mudanças em lógicas de criptografia e hash
// ERRADO: hash personalizado ou algoritmo fraco
function hashPassword(password) {
return md5(password); // MD5 é inseguro!
}
// CORRETO: usar bcrypt
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash(password, 12);
6.3. Identificação de backdoors intencionais ou acidentais
Procure por:
// Padrões suspeitos
- if (user.role === 'superadmin') // bypass não documentado
- // TODO: remover depois dos testes
- debug = true em produção
- Comentários em idioma diferente do projeto
7. Comunicação e Cultura de Segurança no Time
7.1. Como reportar vulnerabilidades de forma construtiva no PR
Exemplo de comentário construtivo:
"Identifiquei que esta consulta SQL está usando concatenação direta (linha 42).
Isso pode permitir SQL Injection. Sugiro usar parâmetros preparados como em:
db.query('SELECT * FROM users WHERE id = ?', [userId])"
7.2. Criação de templates de PR com checklist de segurança
# Template de PR
## Checklist de Segurança
- [ ] Validação de entrada implementada
- [ ] Saída escapada corretamente
- [ ] Sem credenciais hardcoded
- [ ] Dependências verificadas
- [ ] Permissões validadas
- [ ] Logs sem dados sensíveis
7.3. Estabelecimento de gatilhos para revisão de segurança obrigatória
Gatilhos para revisão de segurança obrigatória:
- Mudanças em autenticação/autorização
- Alterações em criptografia/hashing
- Adição de novas dependências
- Mudanças em configurações de rede
- Modificações em pipelines de CI/CD
Conclusão
Conduzir uma revisão de segurança em pull requests é uma habilidade essencial que combina conhecimento técnico, atenção a detalhes e comunicação eficaz. Ao adotar uma abordagem sistemática — desde checklists básicos até ferramentas automatizadas — você transforma cada PR em uma oportunidade para fortalecer a postura de segurança do seu projeto.
Lembre-se: a segurança não é um destino, mas um processo contínuo. Cada revisão é uma chance de aprender, ensinar e construir software mais robusto.
Referências
- OWASP Top 10 - 2021 — As 10 principais vulnerabilidades de segurança em aplicações web, guia essencial para revisão de código.
- GitHub Security Lab - Code Scanning — Documentação oficial sobre como configurar varreduras de segurança automatizadas em pull requests.
- Semgrep Documentation — Ferramenta SAST para encontrar padrões de vulnerabilidades em múltiplas linguagens.
- Snyk - Secure Code Review Best Practices — Guia prático com técnicas e ferramentas para revisão de segurança.
- Bandit - Security Linter for Python — Ferramenta oficial para análise estática de segurança em código Python.
- Dependabot Configuration Guide — Como configurar atualizações automáticas de dependências seguras no GitHub.
- CWE - Common Weakness Enumeration — Catálogo de fraquezas de software, útil para identificar padrões específicos durante a revisão.