Rodando o Brakeman neste repositório
O curso anterior terminou com uma auditoria e uma lista de coisas que uma pessoa precisa procurar. O Brakeman é a parte dessa lista que um programa consegue fazer: ele lê o seu código Rails, segue dados da entrada até chamadas perigosas, e reporta o que encontra.
Ele já rodou neste repositório. O relatório abaixo é o que bin/brakeman --no-pager produziu no dia em que este curso foi escrito, antes das correções das aulas 2, 3 e 7. Rode o mesmo comando neste repositório hoje e ele reporta zero avisos; os seis abaixo ficam porque triar um relatório real é o objetivo inteiro, e este é real.
bin/brakeman --no-pager
== Brakeman Report ==
Rails Version: 8.1.3.1
Brakeman Version: 8.0.4
Checks Run: BasicAuth, ..., SQL, ..., ValidationRegex, ... (79 checks)
== Overview ==
Controllers: 46
Models: 28
Templates: 82
Errors: 0
Security Warnings: 6
== Warning Types ==
Format Validation: 3
SQL Injection: 3
Seis avisos, em cerca de um segundo, sobre cento e cinquenta arquivos. As aulas 2 e 3 percorrem cada um, e no fim deste curso você vai saber quais eram reais, e pode confirmar você mesmo que o mesmo comando reporta zero hoje.
Esse é o formato inteiro de usar a ferramenta: ela produz uma lista, e o valor vem do que você faz com cada entrada.
O que ele é
O Brakeman é um analisador estático: ele lê código sem executá-lo. É isso que o torna rápido e o que determina tudo que ele consegue e não consegue fazer.
| Porque ele não executa o código | Consequência |
|---|---|
| Não precisa de banco, servidor nem dados de teste | Roda em um segundo, em todo pull request |
| Enxerga todo desvio, inclusive os que os testes nunca alcançam | Acha bugs que uma suíte passando esconde |
| Não consegue saber valores em tempo de execução | Reporta o que pode ser entrada de fora, não o que é |
Essa última linha é a origem de todo falso positivo que você vai ver dele, e a razão de a ferramenta reportar um nível de confiança em vez de um veredito.
flowchart LR
S["Seu código"] --> P["Parse para uma árvore sintática"]
P --> T["Rastreia o fluxo: params, cookies, atributos de model"]
T --> C["79 checagens: dado não confiável chega a uma chamada perigosa?"]
C --> W["Avisos, com um nível de confiança"]
W --> H["Uma pessoa decide: real ou não?"]
Como ele decide que algo é perigoso
O Brakeman segue duas coisas e procura pelo encontro delas.
Fontes de dado não confiável: params, cookies, valores de request, e atributos de model definidos a partir de qualquer um desses. Ele propaga por atribuições e chamadas de método, e é por isso que consegue sinalizar um valor a três métodos de distância do controller que o recebeu.
Destinos perigosos: fragmentos de string SQL, render inline:, send, eval, system, redirect_to, raw e html_safe, constantize, File.read. Cada destino tem uma checagem que sabe como é o uso seguro dele.
Um aviso é um caminho de uma fonte a um destino que o Brakeman não conseguiu provar seguro. A formulação importa (não conseguiu provar seguro, não é inseguro) e é o jeito correto de ler o relatório inteiro.
Confiança
| Nível | O que costuma significar |
|---|---|
| High | O padrão é inequívoco: uma construção sabidamente ruim |
| Medium | Entrada de fora provavelmente chega ali, e a ferramenta não tem certeza |
| Weak | Um padrão que vale olhar, muitas vezes tudo bem |
Os seis avisos originais deste repositório eram três High e três Medium, e essa divisão já era informativa: os High são um padrão definido numa validação, e os Medium são um objeto de consulta fazendo algo que o Brakeman não consegue seguir. Confianças diferentes, tipos de trabalho diferentes.
Um aviso de confiança alta não é automaticamente uma vulnerabilidade real, e um fraco não é automaticamente ruído. A confiança descreve o quanto a ferramenta tem certeza sobre o padrão, não o quanto o resultado é explorável. Essa distinção é o assunto da aula 4.
O que ele não consegue achar
Vale dizer cedo, porque uma ferramenta que roda na CI convida à crença de que ela é a revisão de segurança:
- Autorização faltando. Todo o A01 do curso anterior. Uma checagem faltando é código que não foi escrito, e não há o que analisar.
- Falhas de regra de negócio. Um desconto que pode ser aplicado duas vezes é um bug nas suas regras, e o Brakeman não faz ideia de quais são.
- Dependências vulneráveis. Isso é o
bundle audit; o Brakeman analisa o seu código. - Qualquer coisa em tempo de execução. Configuração em produção, uma porta aberta, uma chave vazada.
O Brakeman é uma checagem de uma classe específica de erro (dado não confiável chegando a uma chamada perigosa) e ele é bom nessa classe. Tratá-lo como cobertura de tudo é como um time ganha um selo verde de CI e uma aplicação não auditada.
O que este curso cobre
- Rodando o Brakeman neste repositório (esta aula).
- Os três avisos de validação de formato.
- Os três avisos de injeção de SQL.
- Confiança e triagem.
- Convivendo com ele: CI, linhas de base e arquivos de exclusão.
- Além do Brakeman.
- Juntando tudo: um fluxo de segurança.
Pontos-chave
bin/brakeman --no-pagerrodou 79 checagens sobre este repositório em cerca de um segundo e reportou 6 avisos quando este curso foi escrito; o mesmo comando reporta zero hoje, depois das correções das aulas 2, 3 e 7.- Análise estática lê código sem executá-lo: rápida, completa entre desvios, e incapaz de saber valores em execução.
- Um aviso é um caminho de uma fonte não confiável a um destino perigoso que não pôde ser provado seguro.
- A confiança descreve certeza sobre o padrão, não explorabilidade.
- Ele não acha autorização faltando, falhas de regra de negócio, gems vulneráveis, nem nada sobre produção.
Continue com a aula 2
Os avisos de validação de formato
Mostra por que uma regex de URL sem \z aceita quebras de linha e centraliza a correção num objeto.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.