Lab 47 — WAF e Shield: barrar antes de custar computação
O problema, e a empresa que o tem
A Cadência publicou a API pública no L05: domínio próprio, TLS na borda, estático em cache. Duas semanas depois, o time de finanças pergunta por que a conta de Fargate do mês passado é quase o dobro da anterior — sem que o número de pedidos tenha mudado.
A investigação encontra dois padrões no log do ALB: um script batendo em /api/catalogo a cada poucos segundos, dia inteiro, coletando o catálogo inteiro repetidamente; e uma rajada de tentativas de login com senhas diferentes, contra o mesmo usuário, vinda de um punhado de endereços. Nenhum dos dois é sofisticado — nenhum usa payload malformado, nenhum tenta explorar uma vulnerabilidade conhecida. Os dois só mandam volume.
A primeira reação da equipe é escrever um limitador de taxa dentro da aplicação. Ele funciona — as tentativas de login em excesso passam a receber 429 — e a conta continua subindo. É esse o problema que este laboratório resolve: onde bloquear, não apenas se bloquear.
O que este laboratório NÃO é
Não é detecção de bot sofisticado com fingerprinting de navegador ou análise comportamental — isso é o AWS WAF Bot Control, um complemento pago por requisição inspecionada, que só se justifica quando o bot já rotaciona IP o suficiente para escapar de uma regra de taxa simples. Também não é resposta a incidente de segurança nem investigação de credencial vazada — isso é o L48 e o L50. Este laboratório resolve um problema mais estreito e mais comum: tráfego automatizado comum, não sofisticado, que custa computação antes de ser negado.
O que você vai conseguir fazer
Objetivos verificáveis: cada um se prova com uma métrica ou um comando na seção de implantação, não com a sensação de ter entendido.
- Explicar por que bloquear na aplicação não evita o custo de computação já gasto.
- Diferenciar regra gerenciada (por conteúdo) de regra baseada em taxa (por volume).
- Configurar uma regra de taxa derivada do tráfego real medido, não copiada de um exemplo.
- Testar uma regra nova em modo Count antes de arriscar bloquear cliente legítimo.
- Explicar por que a ordem de prioridade decide qual regra roda, e o efeito disso numa exceção.
- Diferenciar Shield Standard de Shield Advanced e justificar qual se aplica aqui.
- Provar, com uma rajada simulada, que a requisição bloqueada nunca chega ao Fargate.
- Medir custo evitado a partir da métrica de requisições bloqueadas do WebACL.
O que a certificação cobra disto
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| Regra gerenciada vs regra customizada | SAA-C03, SOA-C02 | AWS Managed Rules (core + entradas inválidas) e regra de taxa própria | gerenciada pega padrão conhecido; taxa pega volume, independente do conteúdo |
| Modo Count vs Block | SAA-C03 | toda regra nova sobe em Count antes de virar Block | por que testar sem bloquear é a prática recomendada, não burocracia |
| Ordem de avaliação por prioridade | SAA-C03, SOA-C02 | regra geral de bloqueio antes de exceção nunca deixa a exceção rodar | prioridade menor = avaliada primeiro; ação de término encerra tudo |
| Escopo REGIONAL vs CLOUDFRONT | SAA-C03 | WebACL para CloudFront criado em us-east-1, mesmo alias do certificado do L05 | por que o escopo determina a região do recurso de gestão, não da origem |
| Shield Standard vs Advanced | SAA-C03, SOA-C02 | Standard cobre rede/transporte de graça; Advanced adiciona DRT e reembolso | quando a assinatura paga se justifica, e quando não |
| Rate-based rule: agregação e janela | SAA-C03 | conta por IP numa janela deslizante de 5 min | não é limite exato — é estimativa que pesa requisições recentes |
| Custo de computação vs custo de borda | SOA-C02 | requisição bloqueada nunca vira vCPU-segundo | onde no caminho da requisição o bloqueio é mais barato |
| Destino de log do WAF | SOA-C02 | bucket S3 com prefixo obrigatório e redação de campo sensível | a convenção de nome que a AWS exige, e por que redigir Authorization |
Onde isto costuma ser cobrado errado
A pergunta clássica dá um WebACL com uma regra de bloqueio geográfico de baixa prioridade e uma regra de exceção específica de prioridade alta, e pergunta por que a exceção "não funciona". A resposta não é sobre a exceção estar mal escrita — é sobre ordem: a regra de prioridade menor é avaliada primeiro, encontra correspondência, bloqueia e termina ali. A exceção nunca é alcançada.
Requisitos, e como cada um muda o desenho
Requisito não funcional que não aparece numa linha de configuração é intenção. A coluna da direita é onde cada um deixou marca.
| Requisito | Valor declarado | O que ele decide no desenho |
|---|---|---|
| Reduzir computação gasta com tráfego que não é cliente | declarado pelo enunciado | bloqueio acontece na borda (WAF), não na aplicação — é a decisão central do módulo |
| Erro visível ao cliente legítimo | zero | toda regra nova sobe em Count e só vira Block depois de revisar a amostra |
| Orçamento para segurança | sem assinatura nova | fica em Shield Standard (grátis) + WAF pago por uso; Shield Advanced fica de fora |
| Padrão do tráfego malicioso | volume alto de requisições bem formadas | regra baseada em taxa por IP, não só assinatura de padrão conhecido |
| Auditoria do que foi bloqueado | obrigatória | logging do WebACL para bucket S3, com nome no padrão exigido e campo sensível redigido |
| Origem já fechada contra bypass | herdado do L05 | pré-condição: sem isso, o bot contorna a distribuição e o WAF fica decorativo |
Por que a tabela tem duas linhas de tipo de ameaça, não uma
Regra gerenciada e regra de taxa resolvem os dois padrões que a Cadência mediu, e são requisitos DIFERENTES: um fala de conteúdo conhecido, o outro de volume. Tratar "regra de segurança" como uma coisa só é o que leva a subir apenas uma das duas e deixar metade do problema — scraping bem formado ou tentativa de exploração conhecida — completamente aberta.
Arquitetura mínima: tudo o que chega, é computado
Este é o desenho que a Cadência tem hoje, herdado direto do L05. Ele é legítimo como ponto de partida — a origem já está fechada contra acesso direto — e o laboratório começa por medir o custo que ele deixa passar, porque um número torna o defeito discutível.
- → rajada de requisição, sem limite algum aplicado
- → tráfego legítimo, pelo mesmo caminho do bot
- → encaminha tudo que não está em cache
- → todo request chega à task, inclusive o do bot
- → cada requisição computada vira métrica de CPU e contagem
- Fora da AWS
- Rede e entrega
- Compute
- Gestão e governança
Este é o desenho da Cadência hoje: CloudFront e ALB do L05, sem nenhuma camada de decisão entre o cliente e a task. Ele publica, e a origem já está fechada contra acesso direto — mas nada aqui distingue um bot de um cliente. Percorra os passos e repare onde exatamente o custo é gerado: não na resposta, no processamento que a antecede.
- A distribuição não filtra, só serve. Sem WebACL associado, o CloudFront decide apenas cache hit ou miss e encaminha o resto para a origem. Ele não tem nenhuma noção de "esta requisição parece um bot" — essa decisão simplesmente não existe neste desenho.
- O ALB roteia para alvo saudável, não para requisição legítima. O ALB do L05 já recusa quem não passa pela CloudFront — isso resolve o bypass, não o volume. Ele não inspeciona payload nem conta taxa por IP: só decide se há alvo saudável para encaminhar, e sempre há.
- Cada requisição do bot é computada antes de ser negada. Mesmo que a API devolva 401 por falta de credencial válida, a task já rodou o pipeline de middleware, tentou validar o token e formou a resposta. O 401 é rápido, mas não é grátis: ele já é o resultado de trabalho feito.
- A escala automática reage ao volume errado. Se o serviço tem escala automática por CPU (L06), o volume do bot conta como carga real e dispara novas tasks — o excedente que deveria existir só durante deploy passa a existir também para atender tráfego que nunca deveria ter chegado.
- O sintoma mora na fatura, não no log de erro. CPU alta e uma contagem de 401 dentro do que parece normal escondem o bot dentro do tráfego "saudável". Ninguém vê um erro gritante — vê uma conta de Fargate maior do que o volume de VENDA justificaria.
- Por que alguém publica assim. Porque olhar para WAF parece tarefa de segurança, não de custo, e a fatura só chega trinta dias depois — tarde demais para ligar o pico ao evento que o causou. O L05 já resolveu "alguém acessa direto"; ninguém percebeu que "alguém acessa demais" era um problema diferente e ainda aberto.
Os dois problemas não são o mesmo, e resolver um não resolve o outro
O L05 resolveu "alguém acessa a origem sem passar pela CloudFront" — isso é bypass, e está fechado. Este laboratório resolve "alguém acessa demais, pela CloudFront mesmo" — isso é volume, e continua aberto até este ponto. Confundir os dois leva a achar que o L05 já cobre o problema deste módulo, e ele não cobre: são camadas diferentes de defesa, para ameaças diferentes.
Arquitetura para produção
Cada peça nova abaixo rastreia a uma linha da tabela de requisitos. Se você não conseguir apontar o requisito, a peça é adorno — e este desenho não tem nenhuma.
- → rajada de requisição chega ao ponto de presença
- → tráfego legítimo, sem atrito perceptível
- → avaliação do WebACL antes de decidir o próximo passo
- → só a requisição permitida segue até a origem
- → toda decisão vira registro: bloqueada ou permitida
- → contagem de BlockedRequests e AllowedRequests por regra
- → proteção de camada 3/4, sempre ativa, sem regra para configurar
- → apenas o que passou pelo WebACL chega à task
- Fora da AWS
- Rede e entrega
- Segurança e identidade
- Compute
- Armazenamento
- Gestão e governança
A diferença estrutural não é uma caixa a mais no meio do caminho de sempre: é uma camada de DECISÃO inteiramente nova entre o cliente e a distribuição, com seu próprio destino de registro. Requisição bloqueada não aparece mais como linha na fatura do Fargate — aparece como linha no log do WAF. Percorra os passos: cada peça nova rastreia a um requisito da seção anterior.
- O WebACL avalia antes de a origem ser escolhida. A associação do WebACL à distribuição faz o CloudFront invocar a avaliação do WAF como parte do tratamento da requisição, antes de decidir buscar na origem. É a diferença estrutural em relação ao desenho mínimo: existe agora um ponto de decisão que não existia.
- A ordem das regras decide, não só o conteúdo delas. O WAF avalia por prioridade numérica crescente e para na primeira correspondência com ação de término — Allow ou Block. Uma regra geral de bloqueio com prioridade menor que uma regra de exceção específica faz a exceção nunca ser alcançada: a avaliação já terminou antes de chegar nela.
- Duas regras, dois motivos de bloqueio. O grupo de regras gerenciadas reconhece PADRÃO conhecido de ataque — assinatura de injeção, entrada inválida — mantido pela AWS. A regra de taxa reconhece VOLUME — quantas requisições um mesmo IP mandou numa janela de tempo —, independente de o conteúdo parecer bem formado. O bot deste módulo é pego principalmente pela segunda.
- Bloqueado nunca vira vCPU-segundo. Requisição barrada pelo WebACL termina ali: ela não atravessa o ALB, não chega à task, não abre conexão com dependência nenhuma. É o oposto exato do desenho mínimo, onde tudo era computado antes de ser negado.
- Shield Standard já cobre a camada de rede, de graça. Sem nenhuma configuração deste laboratório, a CloudFront já tem proteção automática contra os padrões mais comuns de ataque volumétrico de rede e transporte. Ele não substitui o WAF: opera numa camada diferente da que o bot deste módulo ataca.
- Toda decisão fica registrada, por regra. Bloqueado e permitido são igualmente registrados no bucket de log, com o identificador da regra que decidiu — é o que permite auditar sem reprocessar nada, e o que alimenta a métrica de custo evitado.
- Por que isso custa menos, e não só é mais seguro. O ganho não é abstrato: cada requisição barrada na borda é uma requisição que o Fargate nunca rodou. O número de bloqueadas na métrica do WebACL é o mesmo número que, no desenho mínimo, teria virado vCPU-segundo cobrado.
A diferença estrutural em relação ao desenho anterior não é uma caixa a mais no meio do caminho de sempre: é uma camada de DECISÃO inteiramente nova, com destino de registro próprio. Tudo o mais — regra gerenciada, regra de taxa, log — existe para que essa decisão seja auditável e testável antes de virar bloqueio real.
O caminho de uma requisição, ponta a ponta
A mecânica que decide se uma requisição vira computação ou vira linha de log é explícita e determinística — não há heurística nem aprendizado envolvido nesta regra, só ordem e correspondência.
A consequência que mais gente erra ao adicionar uma regra
Quando um WebACL já está em produção e alguém adiciona uma regra de exceção — "libere o IP do parceiro X" —, o instinto é dar a ela uma prioridade alta (número grande), pensando em organização: "as regras gerais vêm primeiro, as específicas depois". Isso é exatamente o oposto do que o WAF precisa: como ele avalia da menor prioridade para a maior e para na primeira correspondência terminante, a exceção só funciona se tiver prioridade NUMERICAMENTE MENOR que a regra geral que ela deveria driblar.
O que aparece no log do WAF para cada requisicao avaliada. E este registro que prova qual regra decidiu, sem precisar reprocessar nada.
{
"timestamp": 1754582431618,
"webaclId": "arn:aws:wafv2:us-east-1:111122223333:global/webacl/ffv-lab-borda/...",
"action": "BLOCK",
"terminatingRuleId": "limite-por-ip",
"terminatingRuleType": "RATE_BASED",
"httpRequest": {
"clientIp": "203.0.113.44",
"uri": "/api/login",
"httpMethod": "POST"
},
"rateBasedRuleList": [
{ "rateBasedRuleId": "limite-por-ip", "limitValue": 2000 }
]
}As decisões, e o que se perde em cada uma
📋 A Cadência tem uma API pública (L01, na borda desde o L05) sofrendo scraping de catálogo e tentativas repetidas de login por script. A equipe é pequena, não tem orçamento para assinatura de segurança e precisa de uma correção que não exija reescrever a aplicação.
Resolve o problema no ponto onde ele é mais barato de resolver — a borda, antes de qualquer computação — sem exigir mudança na aplicação nem assinatura paga. O custo é só o do WebACL, das regras e das requisições inspecionadas, e a proteção de rede que a equipe já tinha de graça (Shield Standard) continua cobrindo a camada que o WAF não cobre. Nenhuma peça deste desenho pede aprovação de orçamento maior do que uma linha nova de Terraform.
Alt: Rate limiting dentro da aplicação (middleware ASP.NET Core) — Continua tendo valor como SEGUNDA camada — protege o banco de abuso de um endpoint específico —, mas a requisição já foi computada até chegar ao middleware. Não resolve o problema de custo declarado no enunciado, só o de carga no banco.
Alt: Shield Advanced — Adiciona resposta dedicada (DRT) e reembolso de pico de custo durante ataque volumétrico confirmado, mas é assinatura mensal com compromisso — e o problema aqui é scraping e credential stuffing na camada de aplicação, não um ataque volumétrico de rede que justifique o custo da assinatura.
Alt: Bloquear IP manualmente no security group do ALB — Não escala: um bot minimamente sofisticado rotaciona endereço, e o ALB não inspeciona payload nem conta taxa — cada bloqueio manual é reativo e some na próxima rotação de IP do atacante.
Alt: Restrição geográfica só na CloudFront — Resolve apenas se o tráfego malicioso vier de geografia previsível; bot moderno usa proxy e infraestrutura distribuída globalmente, e a Cadência tem clientes legítimos em várias geografias — a restrição bloquearia os dois.
| Decisão | Escolha | Alternativas | Motivo | O que se perde |
|---|---|---|---|---|
| Onde bloquear | na borda (WebACL na CloudFront) | middleware na aplicação; security group do ALB | requisição bloqueada nunca é computada pelo Fargate | nenhuma visibilidade de negócio sobre o payload específico que foi barrado |
| Tipo de regra contra o scraping/login | gerenciada + taxa, as duas | só gerenciada; só taxa; Bot Control | gerenciada pega padrão de ataque; taxa pega volume — cobrem ameaças diferentes | nenhuma das duas identifica bot sofisticado que imita comportamento humano |
| Ativação da regra de taxa | Count primeiro, Block depois de validar | Block direto | evita bloquear cliente legítimo sem aviso | janela de dias em que o bot continua passando enquanto se observa a amostra |
| Agregação da regra de taxa | por endereço IP | por cabeçalho customizado; por cookie de sessão | é o que a documentação oferece sem exigir que a aplicação já tenha identificador | usuário legítimo atrás de proxy corporativo compartilha contagem com outros |
| Proteção de rede | Shield Standard (nenhuma ação) | Shield Advanced | já incluído, cobre o padrão de ataque volumétrico mais comum | sem DRT dedicado e sem reembolso de pico de custo em ataque confirmado |
| Destino do log do WAF | bucket S3 dedicado | CloudWatch Logs; Kinesis Data Firehose | suficiente para auditoria e consulta posterior, sem peça adicional | menos prático para análise em tempo real do que um stream |
A dívida que este módulo não paga
Uma regra de taxa por IP trata todo IP como um usuário. Um cliente corporativo grande atrás de um único proxy pode estourar o limite sem ter feito nada de errado — é o anti-padrão tratado mais adiante. Resolver isso de verdade exige uma chave de agregação melhor (cookie de sessão, cabeçalho de API key), que pressupõe a aplicação já emitir esse identificador. Enquanto isso, o limite tem de ser calibrado com folga para o maior cliente legítimo conhecido.
Construir: o WebACL, no mesmo alias de região do L05
O escopo CLOUDFRONT exige que o WebACL e tudo que ele referencia sejam criados em us-east-1 — a mesma exigência que o certificado do L05 tem, e pela mesma razão: CloudFront é global, mas parte da API de gestão dos recursos que ele consome fica fixada numa região.
# waf.tf — o WebACL, e por que ele vive na mesma regiao que o certificado do L05
# O escopo CLOUDFRONT exige que o WebACL e tudo que ele referencia — grupo de
# regra, conjunto de IP — sejam criados em us-east-1, independente de onde os
# visitantes estao ou de onde a origem roda. E a MESMA exigencia que o
# certificado ACM do L05 tem, e reaproveitamos o mesmo alias de provider.
resource "aws_wafv2_web_acl" "borda" {
provider = aws.borda # o alias us-east-1 que o L05 ja declara
name = "${var.projeto}-borda"
description = "Regras gerenciadas + taxa por IP, na frente da distribuicao do L05"
scope = "CLOUDFRONT"
# Sem correspondencia terminante em nenhuma regra, a requisicao passa. E a
# rede de seguranca para trafego que nenhuma regra reconhece.
default_action {
allow {}
}
# ── Regra 1: padroes conhecidos de ataque, mantidos pela AWS ───────────────
# Bloqueia por CONTEUDO da requisicao, independente de volume. Prioridade
# menor = avaliada primeiro.
rule {
name = "regras-gerenciadas-core"
priority = 0
override_action {
none {} # usa a acao que cada regra do grupo ja define
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "regrasGerenciadasCore"
sampled_requests_enabled = true
}
}
rule {
name = "regras-gerenciadas-entradas-invalidas"
priority = 1
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesKnownBadInputsRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "regrasGerenciadasEntradasInvalidas"
sampled_requests_enabled = true
}
}
# ── Regra 2: volume, nao conteudo ───────────────────────────────────────────
# Bloqueia por CONTAGEM por IP numa janela deslizante. E a que pega o
# scraping devagar e bem formado que passa pelas duas de cima.
#
# O limite abaixo NAO veio de benchmark de terceiro: veio da medicao de
# trafego legitimo da Cadencia (secao de implantacao). Comece sempre em modo
# COUNT (abaixo) e so troque para BLOCK depois de revisar a amostra.
rule {
name = "limite-por-ip"
priority = 10
action {
count {} # troque para `block {}` depois de validar a amostra — ver provas.sh
}
statement {
rate_based_statement {
limit = 2000 # requisicoes por IP na janela abaixo; NAO e preco, e configuracao
evaluation_window_sec = 300 # 5 min — o padrao; valido tambem 60, 120, 600
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "limitePorIp"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.projeto}-borda"
sampled_requests_enabled = true
}
tags = local.tags
}
output "web_acl_arn" {
value = aws_wafv2_web_acl.borda.arn
description = "ARN do WebACL; e o que a distribuicao referencia em web_acl_id"
}
Subir a regra de taxa direto em Block é o erro mais caro deste módulo
Um limite calibrado errado em modo Block bloqueia cliente real sem nenhum aviso — e o primeiro sinal costuma ser queda de conversão, não um erro visível. Toda regra nova sobe em Count, e só depois de revisar a amostra de requisições que ela capturaria (inclusive as legítimas, se houver) é que a ação muda para Block. Não existe atalho seguro para pular essa etapa.
Construir: a distribuição do L05 aponta para o WebACL
A associação para o escopo CLOUDFRONT não usa um recurso de associação separado — isso só existe para alvo regional, como ALB ou API Gateway. Aqui, o próprio recurso da distribuição referencia o ARN do WebACL.
# borda.tf — a MESMA distribuicao do L05, com uma linha nova
# Para o escopo CLOUDFRONT nao existe `aws_wafv2_web_acl_association` — esse
# recurso e so para alvo REGIONAL (ALB, API Gateway). A associacao acontece
# apontando `web_acl_id` direto na distribuicao, para o ARN do WebACL.
resource "aws_cloudfront_distribution" "borda" {
# ... todos os argumentos que o L05 ja declara (origins, comportamentos,
# certificado, aliases) continuam aqui, sem mudanca nenhuma ...
web_acl_id = aws_wafv2_web_acl.borda.arn # a unica linha nova neste recurso
# Nao ha dependencia explicita necessaria: o Terraform já infere a ordem
# pela referencia ao ARN do WebACL no atributo acima.
}
A distribuição inteira é recriada quando você muda o WebACL?
Não: `web_acl_id` é um atributo mutável da distribuição, e o Terraform aplica isso como atualização, não substituição. Trocar de WebACL, ou remover a associação, não derruba a distribuição nem invalida o cache existente.
Construir: para onde vai cada decisão
Bloqueado e permitido são igualmente registrados, com a regra responsável — é o que permite auditar sem reprocessar nada, e o que alimenta a métrica de custo evitado da seção seguinte.
# log-waf.tf — para onde vai cada decisao, bloqueada ou permitida
# O nome do bucket de destino de log do WAF E OBRIGADO a comecar com
# "aws-waf-logs-" — e uma exigencia documentada, nao uma convencao de estilo.
# Sem o prefixo certo, a configuracao de log falha na hora de aplicar.
resource "aws_s3_bucket" "log_waf" {
bucket = "aws-waf-logs-${var.projeto}-${data.aws_caller_identity.atual.account_id}"
force_destroy = var.ambiente != "prod"
tags = local.tags
}
resource "aws_s3_bucket_public_access_block" "log_waf" {
bucket = aws_s3_bucket.log_waf.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_wafv2_web_acl_logging_configuration" "borda" {
provider = aws.borda # o log de um WebACL CLOUDFRONT tambem se configura em us-east-1
resource_arn = aws_wafv2_web_acl.borda.arn
log_destination_configs = [aws_s3_bucket.log_waf.arn]
# O cabecalho Authorization nunca vai para o log em texto claro. Redaction
# e por campo — aqui, um unico cabecalho — nao existe "redija tudo".
redacted_fields {
single_header {
name = "authorization"
}
}
}
O nome do bucket não é estético
O destino de log do WAF é obrigado a começar com `aws-waf-logs-`. Não é convenção da casa: é exigência documentada, e a configuração de logging falha ao aplicar se o nome não seguir o padrão. É um dos poucos casos desta série em que um nome de recurso tem efeito funcional, não só organizacional.
Construir: a segunda camada, dentro da aplicação
O WAF resolve o custo de computação. Ele não resolve tudo: um limitador dentro da aplicação continua tendo valor como segunda camada, protegendo uma dependência específica — aqui, o pool de conexão do banco no endpoint de login.
// Program.cs — a segunda camada, e por que ela NAO substitui o WAF
var builder = WebApplication.CreateBuilder(args);
// O limitador embutido do ASP.NET Core protege uma dependencia especifica —
// aqui, o pool de conexao do banco no endpoint de login — de ser esgotada por
// tentativa repetida. Ele NAO evita o custo de computacao da requisicao: no
// momento em que este middleware decide negar, o Fargate ja recebeu a
// requisicao, ja passou pelo pipeline ate aqui, e ja foi cobrado por isso.
builder.Services.AddRateLimiter(o =>
{
o.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
o.AddFixedWindowLimiter("login", opcoes =>
{
// Por USUARIO ANONIMO nao existe particionamento identificavel antes
// do login — a janela abaixo e por conexao, o que e mais grosseiro
// que a regra de taxa por IP do WAF. E complementar, nao substituto.
opcoes.PermitLimit = 5;
opcoes.Window = TimeSpan.FromMinutes(1);
opcoes.QueueLimit = 0; // sem fila: negar rapido custa menos que enfileirar
});
});
var app = builder.Build();
app.UseRateLimiter();
app.MapPost("/api/login", async (CredenciaisDto c, IServicoDeLogin login) =>
await login.AutenticarAsync(c) is { } sessao
? Results.Ok(sessao)
: Results.Unauthorized())
.RequireRateLimiting("login");
app.Run();
Por que as duas camadas convivem, e não competem
A regra de taxa do WAF pensa em IP e em volume geral, sem saber qual endpoint está sendo chamado nem o que ele custa internamente. O limitador da aplicação sabe exatamente disso — pode ser mais generoso num endpoint barato e mais rígido num que consulta o banco. Nenhum dos dois substitui o outro: o WAF evita o custo de chegar até aqui; o limitador evita que quem chegou esgote uma dependência específica.
Implantar, e provar com uma rajada simulada
#!/usr/bin/env bash
# simular-bot.sh — a mesma rajada, contra os dois desenhos
set -euo pipefail
URL="https://$(terraform output -raw dominio)/api/login"
TOTAL="${1:-5000}"
# Rajada de POST invalido, paralela, imitando um bot de credential stuffing.
# Contra o desenho MINIMO, cada uma destas chega a task.
seq 1 "$TOTAL" | xargs -P 50 -I{} \
curl -s -o /dev/null -w '%{http_code}\n' \
-X POST "$URL" -H 'Content-Type: application/json' \
-d '{"usuario":"teste","senha":"errada"}' \
> /tmp/codigos-rajada.txt
echo "total de requisicoes: $(wc -l < /tmp/codigos-rajada.txt)"
echo "por codigo:"
sort /tmp/codigos-rajada.txt | uniq -c
# Contra o desenho MINIMO: quase tudo em 401 — cada uma computada.
# Contra o desenho de PRODUCAO: uma fatia crescente em 403 — bloqueada na borda.
Cinco provas. Nenhuma aceita "parece bloqueado" como resultado — cada uma compara um número contra o total enviado, antes e depois de a regra virar Block.
# provas.sh — cinco medições; nenhuma conclusão vem de "parece bloqueado"
WEBACL=$(aws wafv2 list-web-acls --scope CLOUDFRONT --region us-east-1 \
--query "WebACLs[?Name=='ffv-lab-borda'].Id" --output text)
# ── Prova 1: com o desenho MINIMO, a rajada chega inteira à task ────────────
# Rode antes de associar o WebACL. O CPUUtilization da task sobe junto com o
# volume, e a contagem de 401 no ALB bate com o total enviado.
./simular-bot.sh 5000
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS --metric-name CPUUtilization \
--dimensions Name=ServiceName,Value=ffv-lab-api Name=ClusterName,Value=ffv-lab \
--start-time "$(date -u -d '-10 min' +%FT%TZ)" --end-time "$(date -u +%FT%TZ)" \
--period 60 --statistics Maximum --output table
# Esperado: pico visível de CPU coincidindo com a janela da rajada.
# ── Prova 2: com o WebACL em Count, nada é bloqueado ainda ──────────────────
# A métrica de contaria acumula, mas AllowedRequests continua igual ao total.
aws cloudwatch get-metric-statistics --region us-east-1 \
--namespace AWS/WAFV2 --metric-name CountedRequests \
--dimensions Name=WebACL,Value=ffv-lab-borda Name=Region,Value=CloudFront Name=Rule,Value=limitePorIp \
--start-time "$(date -u -d '-10 min' +%FT%TZ)" --end-time "$(date -u +%FT%TZ)" \
--period 60 --statistics Sum --output table
# Esperado: CountedRequests > 0. Nenhuma requisição foi de fato barrada ainda.
# ── Prova 3: depois de trocar para Block, a rajada é interrompida na borda ──
./simular-bot.sh 5000
aws cloudwatch get-metric-statistics --region us-east-1 \
--namespace AWS/WAFV2 --metric-name BlockedRequests \
--dimensions Name=WebACL,Value=ffv-lab-borda Name=Region,Value=CloudFront Name=Rule,Value=limitePorIp \
--start-time "$(date -u -d '-10 min' +%FT%TZ)" --end-time "$(date -u +%FT%TZ)" \
--period 60 --statistics Sum --output table
# Esperado: BlockedRequests próximo do total enviado, acima do limite de 2.000.
# ── Prova 4: a task não viu a parte bloqueada da rajada ─────────────────────
# Compare a contagem de requisições no log do ALB com o total enviado.
aws logs filter-log-events --log-group-name /ecs/ffv-lab-api \
--start-time "$(date -d '-10 min' +%s000)" \
--filter-pattern "/api/login" --query 'length(events)' --output text
# Esperado: bem menor que o total de 5.000 — a diferença é o que a borda reteve.
# ── Prova 5: o cliente legítimo não percebeu nada ───────────────────────────
for i in $(seq 1 20); do
curl -s -o /dev/null -w '%{http_code} ' \
"https://$(terraform output -raw dominio)/api/pedidos/00000000-0000-0000-0000-000000000000"
done
echo
# Esperado: fileira de 200/404 normais, sem nenhum 403 — o cliente não usa o
# endpoint de login em rajada e não se parece com o padrão da regra de taxa.
| Prova | Comando | Resultado que aprova | O que reprova, e o que significa |
|---|---|---|---|
| 1 · A rajada custa CPU no desenho mínimo | métrica de CPUUtilization da task | pico visível coincidindo com a janela da rajada | sem pico: a rajada não gerou carga real, ou a métrica está com dimensão errada |
| 2 · Count registra sem bloquear | métrica CountedRequests da regra de taxa | contagem maior que zero, tráfego continua chegando à task | contagem zero: a regra não está correspondendo — confira o `aggregate_key_type` |
| 3 · Block interrompe na borda | métrica BlockedRequests após trocar a ação | contagem próxima do volume que excedeu o limite configurado | contagem baixa: o limite pode estar alto demais para o volume simulado |
| 4 · A task não viu a parte bloqueada | contagem de eventos no log da aplicação | bem menor que o total enviado na rajada | igual ao total: o WebACL não está associado, ou está em Count, não Block |
| 5 · Cliente legítimo não percebeu nada | fileira de curl em endpoint normal | códigos normais (200/404), nenhum 403 | um 403 aqui é falso positivo: revise o limite ou a chave de agregação |
A prova que fecha o argumento do módulo inteiro
A prova 4 — contagem de requisições que de fato chegaram à task — é a que sustenta o núcleo pedagógico deste laboratório. As provas 1 a 3 mostram que o WAF funciona; a prova 4 mostra que ele funciona onde importa para o problema declarado: no número que aparece na fatura de Fargate, não só na métrica de segurança.
Quebrar de propósito: três falhas e o diagnóstico
As três acontecem de verdade, e as três parecem, à primeira vista, "o WAF não está funcionando". O que as separa é onde exatamente a decisão saiu errada.
| Falha | Como provocar | Sintoma | Onde olhar | Correção |
|---|---|---|---|---|
| Regra esquecida em Count | ative a regra de taxa e nunca troque a ação para Block | a fatura de Fargate continua alta mesmo com o WAF "ativo" | campo Action da regra no console ou no Terraform | trocar para Block depois de validar a amostra — Count não bloqueia nada |
| Exceção com prioridade maior que o bloqueio geral | crie uma regra de bloqueio amplo com prioridade 5 e uma exceção específica com prioridade 20 | cliente com IP fixo recebe 403 mesmo com a regra de exceção presente | ordem numérica das regras no WebACL | dar à exceção prioridade NUMERICAMENTE MENOR que a do bloqueio geral |
| Bot distribuído por trás de muitos IPs | simule a rajada de vários IPs diferentes, cada um abaixo do limite individual | a métrica de bloqueio fica baixa e o volume total continua alto | contagem de requisições por IP nas amostras do WAF | considerar Bot Control, ou agregar por outra chave se a aplicação tiver identificador |
A falha que nenhuma métrica do WAF pega sozinha
Se o security group do ALB aceitar qualquer coisa além do prefixo gerenciado da CloudFront, um atacante que descobre o nome de origem contorna o WAF inteiro — a métrica de bloqueio do WebACL fica ótima, e a fatura continua subindo, porque o tráfego nunca passou pela distribuição. Isso não é falha do WAF: é o pré-requisito do L05 não estando de fato garantido. Confira o security group do ALB sempre que a conta não bater com a métrica de bloqueio.
Uma API recebe um bot que faz login inválido repetidamente. A aplicação responde 401 rapidamente, e a equipe conclui que o custo está sob controle porque nenhuma requisição do bot chega a autenticar. Por que essa conclusão está incompleta?
Segurança: o que este laboratório protege, e o que continua exposto
WAF na borda muda o RISCO, não o elimina. A superfície que sobra depois deste módulo é menor, e vale nomear exatamente o que ainda não está coberto.
| Risco | Probabilidade | Impacto | Controle preventivo | Detecção | Resposta |
|---|---|---|---|---|---|
| Bot consumindo capacidade computacional | alta | médio | regras gerenciadas + regra de taxa na borda | métrica BlockedRequests/AllowedRequests, anomalia de fatura | ajustar limite; se persistir, avaliar AWS WAF Bot Control |
| Regra bloqueando cliente legítimo (falso positivo) | média | alto | testar toda regra em Count antes de Block; revisar amostra | queda de conversão, ou 403 relatado por cliente real | mover a regra de volta para Count, ajustar critério, reativar Block |
| Bypass da origem, ignorando a borda inteira | baixa se o L05 está íntegro | alto | cabeçalho secreto de origem + security group restrito ao prefixo do CloudFront | CloudTrail e log de acesso direto ao ALB, fora do padrão da CloudFront | rotacionar o cabeçalho secreto (Secrets Manager) |
| Ataque volumétrico além do que o Shield Standard cobre | baixa | alto | este laboratório NÃO resolve — é decisão de contratar Shield Advanced | métricas de rede da CloudFront, disponibilidade do serviço | acionar o time de resposta da AWS (DRT), se assinante do Advanced |
| Dado sensível no log do WAF | média | alto | `redacted_fields` no cabeçalho `Authorization` | auditoria de amostra do bucket de log | purgar objeto exposto; ampliar a lista de campos redigidos |
O que o WAF não faz, e por que isso não é uma falha dele
AWS WAF opera na camada de aplicação (7): conteúdo e taxa da requisição HTTP. Ele não substitui autenticação, não substitui autorização por recurso, e não decide se um usuário AUTENTICADO deveria ver um dado específico — isso é o L41 e o L90. Tratar o WAF como controle de acesso é confundir "quem pode bater na porta" com "o que quem entrou pode fazer".
Observabilidade: as perguntas que o painel tem de responder
Um painel de borda tem uma função estreita: dizer, em qualquer momento, o que está sendo barrado e se isso continua fazendo sentido. Métrica que não ajuda nessa decisão pertence a outro painel.
| Pergunta | Métrica ou consulta | O que significa mudar | Limiar inicial |
|---|---|---|---|
| Quanto tráfego está sendo bloqueado? | `BlockedRequests` por regra | volume de ataque, ou regra calibrada errada demais | variação abrupta > 3x a linha de base |
| O bloqueio está pegando cliente legítimo? | `AllowedRequests` vs. conversão de negócio | queda de conversão sem queda de tráfego total é sinal de falso positivo | qualquer queda de conversão correlacionada com mudança de regra |
| Alguma regra específica está disparando demais? | `CountedRequests`/`BlockedRequests` por `metric_name` | aponta qual das regras (gerenciada ou taxa) está reagindo ao tráfego atual | regra individual acima de 50% do total bloqueado |
| O custo de computação caiu de fato? | `CPUUtilization`/`RunningTaskCount` do ECS antes e depois | confirma que o bloqueio na borda reduziu carga real, não só métrica de segurança | redução visível coincidindo com a ativação da regra em Block |
| Existe tráfego chegando fora da CloudFront? | CloudTrail/logs de acesso direto ao ALB | indicaria bypass — o WAF inteiro estaria sendo contornado | qualquer ocorrência é investigável |
A métrica que engana logo depois de ativar Block
Nos primeiros minutos depois de mudar uma regra de Count para Block, `BlockedRequests` sobe de zero para um número alto de uma vez — e isso é o comportamento ESPERADO, não um alarme de ataque em andamento. O alarme certo compara o volume bloqueado contra a contagem que a mesma regra já vinha registrando em Count; um salto ali, sim, é sinal de mudança real no tráfego.
Escala: 10, 10 mil, 1 milhão, e falha de AZ
| Volume | O que acontece com o bloqueio | O que passa a doer | O que fazer |
|---|---|---|---|
| 10 req/s (linha de base da Cadência) | bot é ruído pequeno frente ao tráfego normal | a regra de taxa dificilmente dispara sozinha | regras gerenciadas já cobrem a maior parte; taxa fica como rede extra |
| 10 mil req/s | a regra de taxa passa a ser essencial, não opcional | um bot minimamente distribuído entre poucos IPs escapa do limite individual | calibrar o limite pelo p99 do tráfego legítimo medido, revisar semanalmente |
| 1 milhão req/s | CloudFront e WAF escalam pela própria natureza de rede de borda global | orçamento de unidades de capacidade do WebACL (WCU) por conta é um limite real | consultar Service Quotas antes de adicionar regra nova; considerar Bot Control |
| Falha de AZ na origem | a decisão do WAF não muda em nada | a disponibilidade da origem (ALB/Fargate) é problema do L01/L02, não deste módulo | nada aqui: CloudFront e WAF são serviços de borda, desacoplados da AZ da origem |
O motivo estrutural por que falha de AZ não aparece nesta tabela como risco
WAF e CloudFront não vivem numa AZ: são serviços de borda, distribuídos globalmente pela própria arquitetura da AWS. A decisão de bloquear ou permitir uma requisição não depende, em nenhum momento, de qual AZ da sua origem está de pé. É uma das poucas peças desta série cuja disponibilidade você não projeta — ela já vem assim.
Custo: o que este laboratório acrescenta, e o que evita
É o primeiro laboratório da série em que uma seção de custo fala tanto do que se gasta quanto do que se deixa de gastar — e a segunda parte é o motivo do módulo existir.
| Cenário | Volume | O que acrescenta | Tendência | Otimização |
|---|---|---|---|---|
| Protótipo | tráfego baixo, sem ataque ativo | WebACL base + duas regras gerenciadas + uma de taxa, poucas requisições inspecionadas | desprezível frente ao custo de computação que já existia | nenhuma; regras gerenciadas já cobrem a maior parte sem ajuste |
| Produção pequena | volume da Cadência + rajadas ocasionais de bot | requisições inspecionadas crescem com TODO o tráfego, bloqueado ou não | previsível, cresce linear com o tráfego total | nenhuma regra adicional além das três; monitorar se WCU se aproxima do teto |
| Alta escala | ataque sustentado ou tráfego legítimo muito maior | possível necessidade de Bot Control (cobrança adicional por requisição) ou mais regras | a linha do WAF cresce, mas a do Fargate CAI — é a troca que o módulo defende | escopar regras caras (Bot Control) só nas rotas que precisam, não no site inteiro |
| Dimensão | Cobra por | Cuidado |
|---|---|---|
| WebACL (protection pack) | taxa mensal fixa por WebACL | independe de volume; é o custo de existir, não de usar |
| Regra no WebACL | taxa mensal por regra ativa | grupo de regras gerenciadas conta como conjunto, não uma taxa por regra individual dentro dele |
| Requisição inspecionada | por milhão de requisições, bloqueadas ou permitidas | cresce com TODO o tráfego que passa pela CloudFront, não só com o que é barrado |
| Armazenamento do log do WAF | GB armazenado no S3 | log de 100% do tráfego em alto volume pode custar mais que o problema original — considere retenção curta |
| Bot Control (opcional, não usado neste módulo) | por requisição inspecionada, adicional | só se justifica quando a regra de taxa simples já não segura o bot |
O ganho de custo que aparece na fatura errada de procurar
A economia deste laboratório não aparece na linha "AWS WAF" — aparece na linha "Fargate" ficando menor do que estaria sem o bloqueio na borda. É por isso que a prova 4 da seção de implantação compara requisições na task, não no WAF: o número que importa para o problema declarado é o que NÃO chegou lá.
O teto de WCU é quota, não preço — e vale conferir antes de crescer
Cada regra consome unidades de capacidade do WebACL (WCU), e a conta tem um teto de WCU por WebACL. Regras gerenciadas costumam consumir mais WCU que uma regra de taxa simples como a deste módulo. Antes de adicionar uma quarta ou quinta regra, confira o consumo atual em Service Quotas — descobrir o teto no meio de um incidente é o pior momento possível para descobrir isso.
Well-Architected nos seis pilares
| Pilar | Situação ao fim deste laboratório | Risco que fica | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | regra testada em Count antes de Block, com registro completo da decisão | ninguém revisita o limite quando o padrão de tráfego legítimo muda | revisão periódica agendada da amostra e do limite (L08, observabilidade) | média |
| Segurança | origem inalcançável fora da CloudFront, borda com regra gerenciada e de taxa, Shield Standard automático | cabeçalho secreto do L05 pode vazar ou ser versionado sem querer | rotação do cabeçalho via Secrets Manager (L04) | alta |
| Confiabilidade | regra nova sempre validada em Count antes de Block | nenhum teste automatizado detecta regressão de regra no CI | requisição de smoke test no pipeline confirmando 200 depois de mudança de regra | alta |
| Eficiência de performance | avaliação ocorre no ponto de presença, sem round-trip perceptível | nenhum crítico identificado | nenhuma necessária agora | baixa |
| Otimização de custos | bloqueio na borda evita computação; é o objetivo central do módulo | log de 100% do tráfego pode custar mais que o problema em volume muito alto | filtrar log só do que é bloqueado, ou reduzir retenção do bucket | média |
| Sustentabilidade | menos computação desperdiçada em resposta a tráfego que não é cliente | nenhum novo | nenhuma necessária agora | baixa |
O padrão que se repete nos seis pilares
Em quase toda linha desta tabela, a melhoria seguinte já está numerada em outro laboratório da série. Não é acaso: cada módulo resolve um risco específico e declara, explicitamente, qual risco continua aberto — é assim que a série evita a sensação de "pronto" que nenhum sistema de produção de fato alcança.
Evolução em níveis: o que muda, e o que passa a doer
A terceira arquitetura não é um desenho: é a resposta a QUANDO a defesa de borda simples deixa de bastar. Cada nível resolve um risco e compra outro.
Sem WAF, origem já fechada pelo L05. É onde a Cadência estava antes deste módulo, e continua legítimo enquanto não há tráfego malicioso mensurável.WebACL com regras gerenciadas e regra de taxa por IP, Shield Standard automático, log de decisão.AWS WAF Bot Control para fingerprinting e análise comportamental, escopado só nas rotas que o justificam (L86 trata guardrail e o limite do que um filtro protege).Shield Advanced: DRT dedicado, reembolso de pico de custo em ataque volumétrico confirmado, cobertura ampliada de camada 7.Firewall Manager centralizando WebACLs por várias contas, política obrigatória de regra mínima em toda distribuição nova.Correlacionar amostras do WAF, métricas de negócio e histórico de incidente para sugerir ajuste de limite antes de a fatura subir (é a extensão tratada na seção seguinte).Onde IA entra nesta arquitetura, e onde não entra
Neste módulo, IA não resolve o problema central. A decisão de bloquear por padrão conhecido (regra gerenciada) e por volume (regra de taxa) é determinística e cobre a maioria dos casos reais — um modelo não melhora "esta requisição excedeu 2.000 num IP" nem "este payload casa com uma assinatura conhecida"; isso já é aritmética e correspondência de padrão.
Há um lugar em que IA acrescentaria valor real, e ele é o mesmo do L03: decidir QUANDO revisar e ajustar uma regra, não decidir se bloquear uma requisição específica. O AWS WAF Bot Control, mencionado na evolução em níveis, já usa técnicas de fingerprinting e heurística — mas isso é um produto gerenciado da AWS, não algo que este laboratório constrói.
| Pergunta | Resposta honesta para este módulo |
|---|---|
| Qual problema a IA resolveria? | sugerir ajuste de limite antes de o tráfego legítimo crescer o bastante para começar a bater na regra de taxa |
| Por que uma regra não bastaria? | uma regra com revisão periódica agendada resolve a maior parte disso; IA só se justificaria se o padrão de tráfego legítimo mudasse com frequência maior do que uma revisão manual consegue acompanhar |
| De onde viriam os dados? | log do WAF no S3, métricas de CloudWatch e histórico de conversão de negócio — tudo já existe depois deste laboratório |
| Qual o risco? | ajustar automaticamente o limite numa janela em que o "tráfego legítimo crescendo" é, na verdade, um ataque mais lento e distribuído |
| Por que não agora? | a Cadência tem semanas de dado, não meses; calibrar limite com pouco histórico é decisão com aparência de dado e corpo de palpite |
O uso de IA que parece atraente e é armadilha aqui
Pedir a um modelo para "olhar o tráfego e decidir se é bot" no lugar da regra de taxa troca um critério auditável — 2.000 requisições por IP em 5 minutos, número que qualquer pessoa da equipe consegue verificar — por um probabilístico e opaco. Quando existe um sinal determinístico direto (volume por IP), um modelo só acrescenta latência e a chance de bloquear errado com confiança.
Anti-padrões deste laboratório
| Anti-padrão | Por que alguém faz | Por que é problema | Sintoma em produção | Forma correta | Quando é aceitável |
|---|---|---|---|---|---|
| Regra de bloqueio geral com prioridade menor que a exceção | parece mais simples escrever a regra ampla primeiro, na ordem "natural" de leitura | a exceção nunca é avaliada, porque a regra ampla já terminou a avaliação antes | cliente com IP fixo bloqueado mesmo com a regra de exceção presente no WebACL | dar à exceção prioridade NUMERICAMENTE MENOR que a da regra geral | nunca é aceitável; é sempre defeito, não escolha de design |
| Subir regra nova direto em Block | parece "mais seguro" ativar a proteção imediatamente, sem esperar | falso positivo bloqueia cliente real sem nenhum aviso prévio | queda de conversão sem nenhum erro visível na aplicação | Count primeiro, revisar a amostra, só então Block | nunca em produção; aceitável em ambiente isolado de teste sem tráfego real |
| Rate limit só na aplicação, sem WAF na borda | não exige tocar infraestrutura nova, só código que o time já sabe escrever | a requisição já foi computada antes de o middleware decidir negar | fatura de Fargate sobe junto com o volume de respostas 429 | bloquear na borda primeiro; middleware fica como segunda camada, para dependência específica | quando não há CDN nem edge na frente — não é o caso aqui, por causa do L05 |
| Confiar só no Shield contra bot de aplicação | "Shield" soa como a resposta completa para qualquer tipo de ataque | Shield Standard cobre rede e transporte (L3/4); scraping e credential stuffing são L7 | ataque de aplicação continua passando mesmo com Shield "ativo" e sem alerta nenhum | WAF trata L7; Shield Advanced complementa com DRT e reembolso, não substitui o WAF | nunca; são camadas diferentes, sempre complementares |
| Bot Control ligado em todas as rotas sem escopo | parece a proteção "mais completa" do catálogo de regras gerenciadas | cobra por requisição inspecionada, e rota estática (imagem, JS) não precisa dessa análise | fatura do WAF cresce mais que o problema original que se tentava resolver | escopar a regra por rota que de fato precisa da análise, não aplicar globalmente | quando o risco de bot sofisticado é uniforme em todo o site — raro |
| Log de 100% do tráfego sem redação nem filtro | é o padrão que aparece primeiro ao configurar logging pelo console | guarda dado sensível (Authorization) e cresce sem necessidade real | bucket de log maior que o bucket de dado de produção da aplicação | `redacted_fields` no cabeçalho sensível; considerar retenção curta | ambiente de baixíssimo volume, onde o custo de armazenamento é irrelevante |
O anti-padrão mais caro da lista, e por que ele passa despercebido
Bot Control ligado sem escopo é o único item desta tabela que gera fatura crescente mesmo fazendo exatamente o que promete: bloquear bot melhor. O problema não é eficácia — é abrangência. Inspecionar uma rota estática que nenhum bot sofisticado ataca custa por requisição, o tempo todo, para um benefício que só existe nas rotas que de fato precisam dessa análise.
Quando algo não funciona
| Sintoma | Causa provável | Como investigar | Onde olhar | Correção |
|---|---|---|---|---|
| Fatura de Fargate continua alta com WAF "ativo" | a regra de taxa está em Count, nunca bloqueia de fato | confira a ação configurada na regra | campo Action da regra, no console ou no Terraform | mudar para Block depois de validar a amostra |
| Cliente com IP fixo bloqueado mesmo com regra de exceção | prioridade da regra de bloqueio geral é menor (avaliada antes) que a da exceção | compare a ordem numérica das regras no WebACL | lista de regras por prioridade | dar prioridade menor à exceção |
| Bot continua passando apesar da regra de taxa | bot distribui requisições entre muitos IPs, cada um abaixo do limite individual | conte requisições por IP nas amostras do WAF | campo de IP de origem nas sampled requests | considerar Bot Control, ou agregar por chave diferente se disponível |
| Usuários legítimos atrás do mesmo proxy corporativo bloqueados em massa | a regra de taxa agrega por IP, e o proxy concentra muitos usuários num único IP | compare quantos usuários distintos correspondem ao IP bloqueado | log de acesso da aplicação vs. amostra do WAF | elevar o limite para aquele intervalo, ou agregar por outra chave |
| Métrica de bloqueio sobe, mas a fatura de Fargate não cai | existe caminho direto até o ALB, fora da CloudFront | confira se o security group do ALB aceita algo além do prefixo do CloudFront | regras do security group do ALB, CloudTrail de acesso à origem | revisar o cabeçalho secreto e a lista de prefixo gerenciada do L05 |
| WebACL não aparece como opção ao associar a distribuição | o WebACL foi criado fora de us-east-1 | confira a região em que o recurso foi criado | console ou CLI com `--region us-east-1 --scope CLOUDFRONT` | recriar em us-east-1; não há como mover um WebACL de região |
| Log do WAF não aparece no bucket configurado | nome do bucket não começa com `aws-waf-logs-` | confira o nome exato do destino configurado | atributo `log_destination_configs` no Terraform | renomear o bucket seguindo a convenção obrigatória |
| Alarme de bloqueio dispara para tráfego que parece legítimo | regra gerenciada com falso positivo para um padrão específico do seu produto | identifique qual regra específica do grupo gerenciado correspondeu | campo `terminatingRuleId` nas sampled requests | excluir a regra específica via override, ou adicionar uma condição de escopo |
A pergunta que resolve metade destes casos
Antes de mexer em regra, pergunte: a métrica do WAF e a fatura do Fargate contam a mesma história? Se o WAF mostra bloqueio alto e a fatura não cai, o problema é bypass — o tráfego não está passando pela borda. Se a fatura cai mas cliente real reclama, o problema é falso positivo — a regra está bloqueando demais.
Limpeza: o que o destroy não leva
Este laboratório acrescenta poucos recursos novos sobre o L05, e a maioria deles não cobra parado. O que sobra depois do `terraform destroy` é pequeno, mas vale a prova.
# 1. Derrube o que o Terraform administra.
terraform destroy -auto-approve
# 2. BUCKET DE LOG DO WAF: se `force_destroy` estiver desligado, o destroy falha
# com o bucket cheio, e ele fica de pe cobrando GB armazenado.
aws s3 ls | grep aws-waf-logs
aws s3 rm "s3://aws-waf-logs-ffv-lab-<conta>" --recursive 2>/dev/null || true
aws s3api delete-bucket --bucket "aws-waf-logs-ffv-lab-<conta>" 2>/dev/null || true
# 3. Confirme que o WebACL, criado em us-east-1, realmente saiu — regiao errada
# na consulta e o motivo mais comum de "esqueci de apagar".
aws wafv2 list-web-acls --scope CLOUDFRONT --region us-east-1 \
--query "WebACLs[?Name=='ffv-lab-borda']" --output table
# 4. O que veio do L05 e do L01 continua cobrando por hora se voce nao for
# seguir para outro laboratorio: CloudFront, ALB, RDS, NAT Gateway.
# Rode a limpeza deles tambem.
# 5. Prova final: nada com o nome do projeto de pe.
aws resourcegroupstaggingapi get-resources \
--tag-filters Key=Projeto,Values=ffv-lab \
--query "ResourceTagMappingList[].ResourceARN" --output table| Recurso | Sai no destroy? | Cobra parado? | Por que fica |
|---|---|---|---|
| WebACL e regras | sim | não | nenhum estado residual sobrevive à remoção do recurso |
| Bucket de log do WAF | só com `force_destroy` | sim, GB-mês | o destroy falha se houver objeto dentro; log acumulado em alto volume não é pequeno |
| Configuração de logging do WebACL | sim | não | é configuração associada ao WebACL, não recurso independente |
| Shield Standard | não se aplica | não | não é um recurso que se cria nem se destrói — é proteção automática da conta |
| O que vem do L05 (CloudFront, ALB) e do L01 (RDS, NAT) | depende do módulo | sim, por hora ou por GB | este laboratório não os recria; confira a limpeza deles separadamente |
Resumo: problema, peça e motivo
| Problema | Peça | Por que ela, e não outra |
|---|---|---|
| Fatura de Fargate sobe sem venda correspondente | WebACL na borda | requisição bloqueada nunca vira vCPU-segundo — o custo evitado é o objetivo direto |
| Scraping com requisição bem formada | regra baseada em taxa por IP | não depende de conteúdo malicioso, só de volume — pega o que a regra gerenciada não pega |
| Tentativa de exploração de vulnerabilidade conhecida | AWS Managed Rules | mantidas e atualizadas pela AWS; cobre padrão de ataque sem escrever regra própria |
| Risco de bloquear cliente legítimo sem aviso | Count antes de Block | testar sem risco é o que separa uma regra confiável de uma aposta |
| Exceção que nunca é avaliada | prioridade numérica correta | ordem de avaliação decide, não a intenção de quem escreveu a regra |
| Ataque volumétrico de rede | Shield Standard | já incluído, cobre a camada que o WAF não cobre, sem custo adicional |
| Auditoria do que foi bloqueado | log do WebACL em S3 | registro por regra, sem precisar reprocessar nada para saber o que aconteceu |
| Dado sensível em log de tráfego | `redacted_fields` | redação por campo específico, não a escolha entre logar tudo ou nada |
| Falha | O que a protege | O que ela NÃO protege |
|---|---|---|
| Requisição computada antes de ser negada | bloqueio na borda (WebACL) | bot que já contornou a origem por bypass — depende do L05 estar íntegro |
| Padrão conhecido de ataque | AWS Managed Rules | volume alto de requisição bem formada |
| Volume alto de um mesmo IP | regra baseada em taxa | bot distribuído por muitos IPs |
| Ataque volumétrico de rede/transporte | Shield Standard | ataque de aplicação (camada 7) |
| Cliente legítimo bloqueado sem aviso | validação em Count antes de Block | regra recalibrada errado depois de meses sem revisão |
| Bypass da distribuição | cabeçalho secreto + prefix list do L05 | security group do ALB alterado manualmente fora do Terraform |
- A requisição chega ao ponto de presença da CloudFront.
- Como há WebACL associado, o WAF avalia as regras por prioridade crescente.
- A regra gerenciada corresponde a padrão conhecido de ataque, ou não corresponde a nada.
- A regra de taxa conta o IP na janela de 5 minutos e compara com o limite.
- Se alguma regra tem ação de término (Allow ou Block) e corresponde, a avaliação para ali.
- Bloqueada: a decisão é registrada no log e a requisição nunca chega ao ALB.
- Permitida: segue para o ALB, que já recusa quem não veio da CloudFront (L05).
- Só então a task processa a requisição — e só então existe custo de computação.
- A métrica de BlockedRequests do WebACL é o número que devolve o custo evitado.
Perguntas frequentes
❓ Preciso do Shield Advanced para me proteger de bot que consome computação?
❓ Por que bloquear na aplicação não resolve o problema de custo do bot?
❓ Qual a diferença entre regra gerenciada e regra baseada em taxa no AWS WAF?
❓ Por que a ordem das regras no WebACL importa tanto?
❓ O que o modo Count faz, e quando devo usar?
❓ Um WebACL para o CloudFront tem que ser criado numa região específica?
❓ Uma regra de taxa bloqueia o IP para sempre depois que ele estoura o limite?
❓ Habilitar o WAF adiciona latência perceptível à resposta?
Fixando
Um WebACL tem duas regras: uma bloqueia todo tráfego de fora do Brasil (prioridade 5) e outra permite explicitamente o IP fixo de um parceiro internacional (prioridade 20). O parceiro reporta que continua recebendo 403. Qual é a causa mais provável?
A equipe da Cadência configura uma regra de taxa agregando por endereço IP, com limite de 2.000 requisições em 5 minutos. Depois de ativar, vários funcionários de um grande cliente corporativo — todos atrás do mesmo proxy da empresa — começam a receber 403. O que está acontecendo, e qual é a correção mais direta?
Conhecimentos, próximo módulo e documentação
| Item | Conteúdo |
|---|---|
| Conhecimentos anteriores necessários | L05 no ar (CloudFront, ALB, origem fechada por cabeçalho secreto e prefix list); noções básicas de HTTP, Terraform e ASP.NET Core |
| Conhecimentos adquiridos | por que bloquear na aplicação não evita custo de computação já gasto; a diferença entre regra gerenciada e regra de taxa; ordem de avaliação por prioridade e o efeito numa exceção mal posicionada; Count como teste seguro antes de Block; Shield Standard vs Advanced |
| Limitação que fica | a regra de taxa por IP trata proxy corporativo como um usuário só, e bot distribuído por muitos IPs escapa do limite individual — os dois exigem ajuste manual ou Bot Control |
| Próximo exemplo recomendado | L90 — prompt injection e vazamento entre inquilinos. Reutiliza a camada de borda construída aqui e trata a fronteira seguinte: uma requisição que passa pelo WAF mas carrega intenção maliciosa dentro do conteúdo que um sistema de IA processa |
| Também habilitado por este módulo | L86 (guardrails: o que protege e o que não protege) usa a mesma distinção entre filtro de borda e controle de aplicação; L48 (detecção com GuardDuty/Security Hub) consome o log deste WebACL como uma das fontes de achado |
| Data da última validação técnica | 7 de agosto de 2026 |
Documentação oficial consultada: Using rate-based rule statements in AWS WAF e Rate-based rule high-level settings — a janela de avaliação (60 a 600 s, padrão de 5 min) e o algoritmo de estimativa que pesa requisições recentes; Setting rule priority e How AWS WAF handles rule and rule group actions — a ordem de avaliação por prioridade crescente e a diferença entre ação terminante (Allow/Block) e não terminante (Count); AWS Shield Standard overview e AWS Shield Advanced overview — o que cada nível cobre e o reembolso de custo do Advanced; Resources that you can protect with AWS WAF — a exigência de us-east-1 para o escopo CLOUDFRONT; e Logging destinations for AWS WAF — a convenção obrigatória de nome do bucket e o mecanismo de redação de campo. Valores de preço não aparecem neste módulo por decisão: use o AWS Pricing Calculator, porque preço varia por região e envelhece mais rápido que o conteúdo.
O que não foi verificado, e você deve conferir na sua conta
O limite de 2.000 requisições por IP em 5 minutos usado nos exemplos deste módulo é derivado do volume fictício da Cadência, não de uma recomendação oficial de valor — ele deve ser calibrado a partir do PRÓPRIO tráfego legítimo medido, com folga para o maior cliente conhecido atrás de IP compartilhado. A documentação também não deixa explícito, nas páginas consultadas, se a avaliação do WAF ocorre de forma idêntica em cache hit e cache miss da CloudFront para todo tipo de comportamento de cache; trate isso como não confirmado e, se depender dessa distinção, valide diretamente no seu ambiente.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…