O ataque que você vai realmente sofrer
Leia a ação de login deste repositório:
# app/controllers/auth/sessions_controller.rb
def create
result = Auth::AuthenticateUserService.call(
email: params[:email],
password: params[:password]
)
if result.success
# Read before reset_session clears it.
pending_link = session[:pending_partner_link]
reset_session
session[:user_id] = result.user.id
::Auth::CompletePartnerLinkService.call(user: result.user, pending: pending_link)
redirect_to post_sign_in_path(result.user), notice: t("auth.flash.welcome_back", username: result.user.username)
else
@error = result.error
flash.now[:alert] = @error
render :new, status: :unprocessable_content
end
end
Esse é um código cuidadoso. Usa um objeto de serviço, chama reset_session no sucesso (a defesa contra fixação de sessão do curso de OWASP) e devolve um erro genérico. Toda decisão nele está certa.
E nada nele conta. Sozinha, esta ação aceitaria dez mil tentativas por minuto de um endereço. Neste repositório a contagem mora em outro lugar: o Identity::User bloqueia uma conta depois de falhas repetidas, e o Rack::Attack limita logins por IP e por e-mail, como a aula 7 mostra. Este curso é sobre por que essas camadas existem e como escolhê-las.
O que chega
Não é um ataque direcionado. O que chega é automatizado e indiscriminado:
| Tráfego | O que quer | O que te custa |
|---|---|---|
| Credential stuffing | Senhas reaproveitadas de outros vazamentos | Trabalho de bcrypt por tentativa, e contas |
| Raspadores | O seu catálogo, em velocidade | Carga de banco, e o seu conteúdo em outro lugar |
| Scanners de vulnerabilidade | /wp-admin, /.env, /.git/config |
Ruído, e um acerto se algo estiver exposto |
| Enxurrada de redefinição de senha | Nada: ou a reputação do seu e-mail | A sua pontuação de remetente, gasta por outra pessoa |
| Enxurrada de cadastro | Contas grátis, para spam | Dados lixo e moderação |
O credential stuffing merece a ênfase por causa de uma assimetria. Quem ataca não está adivinhando senhas; tem uma lista de pares reais de e-mail e senha de um vazamento alheio e está checando quais funcionam no seu site. Uma política de senha forte não ajuda: a senha é forte, e é também a que a pessoa reaproveitou.
As únicas defesas são limitar tentativas, um segundo fator, e checar contra senhas sabidamente vazadas. Este curso é a primeira.
O que é limitação de taxa
Um contador e um limiar: esta identidade, este número de requisições, nesta janela, e então pare.
flowchart LR
R["Requisição"] --> D["Identificar: IP? conta? os dois?"]
D --> C["Contar na janela atual"]
C --> T{"passou do limite?"}
T -->|"não"| A["deixa passar"]
T -->|"sim"| B["429, com Retry-After"]
Três decisões nesse diagrama, e cada uma é uma aula: a identidade é a aula 4, o armazenamento do contador é a aula 5, a resposta é a aula 6.
O que não é
Ser claro sobre isso cedo evita uma categoria de confiança mal colocada:
- Não é defesa contra ataque distribuído. Dez mil endereços fazendo dez requisições cada derrotam um limite por IP por completo. Limitação de taxa encarece um ataque de uma máquina; uma botnet é outro problema.
- Não é autorização. Limitar com que frequência alguém chama um endpoint não diz nada sobre se ela deveria poder.
- Não substitui um segundo fator. Ela desacelera o credential stuffing. A autenticação de múltiplos fatores o torna inútil.
- Não é de graça. Toda requisição passa a tocar um contador, que é o assunto da aula 5 e é mais caro neste repositório do que você imagina.
As três camadas
A limitação de taxa acontece em três lugares, e eles são complementares e não alternativos:
| Camada | Exemplo | Enxerga | Não enxerga |
|---|---|---|---|
| Borda | Cloudflare, uma CDN, um WAF de nuvem | Volume, antes de te custar qualquer coisa | Quem está logado, o que a ação significa |
| Proxy | nginx, kamal-proxy | Requisições, por host | Conceitos da aplicação |
| Aplicação | rate_limit do Rails, Rack::Attack |
A conta, a ação, o desfecho | Tráfego que ela nunca recebe |
A borda é onde volume é mais barato de parar, porque a requisição nem chega aos seus servidores. A aplicação é onde mora o significado: isto é um login que falhou para esta conta é uma frase que só o seu código consegue formar.
Um limite geral de borda de 100 requisições por minuto por IP não impede 20 tentativas de login por minuto, que é bastante para credential stuffing. Essa lacuna é por que este curso é sobre a camada de aplicação, e a aula 6 volta a como as camadas se combinam.
O que este curso cobre
- O ataque que você vai sofrer (esta aula).
- O
rate_limitembutido do Rails. - Fundamentos do Rack::Attack.
- Escolhendo o discriminador.
- O armazenamento, e o que ele custa.
- Respostas, observabilidade e ajuste.
- Juntando tudo: protegendo esta aplicação.
Pontos-chave
- A ação de login deste repositório é bem escrita e não conta nada sozinha; os limites moram num bloqueio de conta e no Rack::Attack.
- O credential stuffing usa senhas reais de outros vazamentos, então uma política de senha forte não defende contra ele.
- Limitação de taxa é uma identidade, uma contagem, uma janela e um limiar: três disso são aulas seguintes.
- Ela não impede ataques distribuídos, não substitui autorização nem um segundo fator.
- A borda para volume barato; a aplicação é a única camada que sabe o que uma requisição significa.
Continue com a aula 2
O rate_limit embutido do Rails
Mostra o método rate_limit embutido do Rails, suas opções, o uso na redefinição de senha e seus limites.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.