Buzeli
buzeliSoluções Digitais
Segurança

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

Um upload de arquivo sendo barrado por uma parede de WAF com um limitador de tamanho de 8KB

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:

Copiar
action=BLOCK  uri=/wp-admin/async-upload.php  method=POST
terminatingRule=AWS-AWSManagedRulesCommonRuleSet  ruleId=SizeRestrictions_BODY
bodySize=85701

A 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:

Copiar
{
  "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.