Upload de mídia bloqueado com 403: o limite de 8 KB do AWS WAF que ninguém documenta
Publicado em 19 de julho de 2026

O sintoma: 403 só no upload, e só acima de um tamanho
Editores de um WordPress relatavam erro ao subir imagens — mas só algumas. Arquivos pequenos passavam; os maiores davam 403. O site público estava normal. Olhando o log do WAF, a requisição bloqueada era clara:
action=BLOCK uri=/wp-admin/async-upload.php method=POST
terminatingRule=AWS-AWSManagedRulesCommonRuleSet ruleId=SizeRestrictions_BODY
bodySize=85701A causa: SizeRestrictions_BODY e o limite de inspeção de 8 KB
A regra gerenciada SizeRestrictions_BODY (do Common Rule Set da AWS) bloqueia corpos de requisição grandes. Mas há uma sutileza: o WAF só inspeciona os primeiros 8 KB do corpo (16 KB em planos superiores). Um upload de 84 KB estoura tanto o limite da regra quanto o de inspeção — e é barrado antes de chegar ao WordPress.
O Common Rule Set é ótimo por padrão, até a regra de tamanho tratar um upload legítimo de imagem como ataque. Toda app que recebe upload precisa de uma exceção consciente para as rotas de upload.
O fix exige um label-match — na ordem certa
A saída não é desligar a regra (perde a proteção). É deixá-la em modo COUNT para gerar um label, e então criar uma regra própria, de prioridade MENOR, que permite a requisição quando o label está presente E o path é de upload. Se a managed rule ficar em modo BLOCK, ela bloqueia ANTES da sua regra rodar — por isso o COUNT é obrigatório.
A armadilha do CloudFront WAF: sem RegexMatchStatement
Para casar o path /wp-admin/async-upload.php, o instinto é usar regex. Mas o WAF associado a uma distribuição CloudFront (escopo CLOUDFRONT/global) NÃO suporta RegexMatchStatement. É preciso usar ByteMatchStatement com ENDS_WITH/STARTS_WITH. E, ao enviar via --cli-input-json, a SearchString vai em base64:
{
"Name": "allow-wp-upload",
"Priority": 0,
"Action": { "Allow": {} },
"Statement": {
"ByteMatchStatement": {
"SearchString": "L3dwLWFkbWluL2FzeW5jLXVwbG9hZC5waHA=",
"FieldToMatch": { "UriPath": {} },
"PositionalConstraint": "ENDS_WITH",
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }]
}
},
"VisibilityConfig": { "SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true, "MetricName": "AllowWpUpload" }
}Detalhes que custam tempo: o campo Description tem comprimento mínimo 1 (string vazia falha na validação), e labels só funcionam se a managed rule que os gera estiver em COUNT, não em BLOCK. Recursos Pro (overrides, labels, SizeConstraint, ByteMatch em Cookies) não existem no plano Free.
Lições
1. SizeRestrictions_BODY barra uploads grandes por padrão. Toda rota de upload precisa de exceção; o WAF inspeciona só os primeiros 8 KB do corpo.
2. Managed rule tem que ir para COUNT p/ o label funcionar. Em BLOCK, ela termina a avaliação antes da sua regra de allow.
3. CloudFront WAF não tem RegexMatchStatement. Use ByteMatch (ENDS_WITH/STARTS_WITH); via CLI/JSON, a SearchString é base64.
Conclusão
O Common Rule Set protege bem por padrão — e por padrão também trata o upload de imagem do seu editor como payload suspeito. O 403 não era ataque nem bug do WordPress: era a regra de tamanho fazendo o trabalho dela sem saber do contexto. Um label-match na ordem certa, com ByteMatch porque o CloudFront não aceita regex, devolve o upload sem abrir a guarda.