Intermediário Aplicação e Código
Code review focado em segurança — o que procurar, checklists, pull request
SAST e DAST automatizam parte da análise, mas não substituem revisão humana. Um revisor experiente percebe falhas de lógica de negócio que nenhuma ferramenta detecta: verificações de autorização incorretas, fluxos de estado exploráveis, decisões arquiteturais inseguras.
Mentalidade do Revisor de Segurança
Ao revisar código, pergunte-se:
- De onde vêm esses dados? Quem controla essa entrada?
- O que acontece se esse valor for inesperado (null, vazio, muito grande, malformado)?
- Quem tem permissão para executar essa ação? Está sendo verificado?
- O que este código expõe — dados, erros, estado interno?
- Existe algum segredo ou credencial visível?
O Que Procurar por Categoria
Autenticação e Autorização
# Ruim — verifica apenas autenticação, não autorização
@app.route("/api/orders/<order_id>")
@login_required
def get_order(order_id):
return Order.get(order_id) # qualquer usuário logado vê pedido alheio
# Bom — verifica se o recurso pertence ao usuário atual
@app.route("/api/orders/<order_id>")
@login_required
def get_order(order_id):
order = Order.get(order_id)
if order.user_id != current_user.id:
abort(403)
return order.to_dict()
Injeção (SQL, Shell, LDAP)
# Ruim — interpolação de string em query
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
# Ruim — shell injection
os.system(f"convert {filename} output.pdf")
# Bom — parametrizado
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
# Bom — lista de argumentos, sem shell
subprocess.run(["convert", filename, "output.pdf"], check=True)
Gerenciamento de Sessão e Tokens
- Tokens JWT verificam assinatura antes de usar payload?
- Sessão é invalidada no logout?
- Cookies têm flags
HttpOnly,Secure,SameSite? - Tokens de reset de senha expiram?
Criptografia
- MD5 e SHA1 são usados para hashing de senha? (errado — use bcrypt/argon2)
- Chaves criptográficas estão hardcoded?
- TLS está configurado corretamente (sem versões antigas, sem cipher suites fracas)?
Upload de Arquivos
# Ruim — aceita qualquer extensão
filename = request.files["file"].filename
file.save(f"/uploads/{filename}")
# Bom — whitelist de extensões + nome gerado pelo servidor
ALLOWED = {".jpg", ".png", ".pdf"}
ext = Path(filename).suffix.lower()
if ext not in ALLOWED:
abort(400)
safe_name = str(uuid.uuid4()) + ext
file.save(f"/uploads/{safe_name}")
Checklist para PR de Segurança
Entrada de Dados
- Todo input externo é validado antes do uso
- Não há concatenação de input em queries, comandos ou templates
- Uploads validam tipo real (magic bytes), não só extensão
Controle de Acesso
- Toda rota protegida verifica autenticação E autorização
- Referências a recursos verificam propriedade (IDOR prevention)
- Funções administrativas têm verificação de papel (role)
Dados Sensíveis
- Nenhum segredo ou credencial no diff
- PII não aparece em logs
- Dados sensíveis em trânsito usam TLS
Tratamento de Erros
- Exceções capturadas e logadas — sem stack trace para o cliente
- Comportamento de falha nega acesso por padrão
Dependências
- Nenhuma dependência nova com CVE conhecido
- Lock file atualizado
Como Dar Feedback no Pull Request
Seja específico e objetivo:
# Ruim
"Esse código tem problema de segurança."
# Bom
"Linha 47: a query usa interpolação de string com `user_id`.
Se `user_id` vier da query string sem validação (linha 12),
isso abre SQL Injection. Use prepared statement:
cursor.execute('SELECT * FROM orders WHERE user_id = %s', (user_id,))
Ref: OWASP SQL Injection Prevention."
Aponte: a linha, o risco concreto, e a solução. Sem julgamento de pessoa — foco no código.
Ferramentas de Apoio à Revisão
- Semgrep — rode localmente no diff antes de abrir o PR.
- CodeClimate / SonarCloud — análise automática integrada ao GitHub.
- GitHub Code Scanning — executa SARIF findings diretamente no PR.
Code review de segurança não é auditoria formal. É a última linha de defesa humana antes do merge. Faça parte da cultura do time, não exceção.