fizz.today

Your WAF ‘10MB limit’ is actually an 8KB limit

A 631KB PDF upload returned 403 from the ALB. The WAF rule said “block requests over 10MB.” The file was nowhere near 10MB.

I checked the WAF logs. The terminating rule was MaxBodySize10MB, a SizeConstraintStatement — body greater than 10,485,760 bytes, action block. Straightforward, except the Body field had OversizeHandling = "MATCH", and that one setting inverted the entire rule.

What OversizeHandling actually does

AWS WAF can only inspect the first 8KB of a request body when the resource is an ALB or AppSync endpoint. That limit is fixed — you can’t raise it. (CloudFront, API Gateway, Cognito, App Runner, and Verified Access default to 16KB and can go up to 64KB in 16KB increments, but ALB is stuck at 8KB.)

When the body exceeds the inspection limit, WAF has to decide what to do without having read the full content. Three options:

With MATCH, WAF never evaluates the size constraint. It sees a body larger than 8KB, stops reading, and declares “this matches your GT 10MB rule” — even though the body is 631KB. The actual size is irrelevant because WAF never measured it. The inspection limit is the only threshold that matters.

If you’re on ALB — which most EKS deployments are — every file upload over 8KB triggers this behavior. For a compliance platform that processes policy documents and evidence PDFs, that’s every single upload.

Changing MATCH to CONTINUE fixed the custom rule:

size_constraint_statement {
  field_to_match {
    body {
      oversize_handling = "CONTINUE"  # was "MATCH"
    }
  }
  comparison_operator = "GT"
  size                = 10485760
}

CONTINUE tells WAF to inspect what it can and evaluate the constraint against the partial data. For a size check, the Content-Length header is available regardless of body inspection limits, so WAF can still evaluate actual size.

The managed rules that compound this

Fixing my custom rule wasn’t enough. The AWSManagedRulesCommonRuleSet includes two rules that also inspect the body, and both of them blocked the same upload for different reasons.

SizeRestrictions_BODY is AWS’s own body size rule inside the managed ruleset. Its internal OversizeHandling behavior isn’t configurable — you get whatever AWS chose when they wrote it. If it treats oversize bodies as matching (and it does), your upload is blocked even after you fix your custom rule. I set it to count mode — log the match without blocking — so uploads go through while the rest of the managed ruleset stays active.

CrossSiteScripting_BODY inspects request bodies for XSS patterns. PDF binary content is full of byte sequences that look like script injection to a pattern matcher. Every PDF upload triggers a false positive. Same fix: count mode, let it log, don’t let it block.

Both overrides live in the managed_rule_group_statement as rule_action_override blocks:

managed_rule_group_statement {
  name        = "AWSManagedRulesCommonRuleSet"
  vendor_name = "AWS"

  rule_action_override {
    action_to_use { count {} }
    name = "SizeRestrictions_BODY"
  }

  rule_action_override {
    action_to_use { count {} }
    name = "CrossSiteScripting_BODY"
  }
}

Three rules, three blockers, one upload. The custom size constraint was the one I wrote and should have gotten right. The managed rules were the ones I didn’t think to check because I assumed AWS’s defaults would be sensible for a file upload behind an ALB. They’re sensible for protecting against malicious payloads. They’re not sensible for a platform that exists to receive documents.


Docs: Oversize web request components · Body API reference · Body inspection considerations

#aws #waf #alb #platformengineering