A WAF is a virtual patch: it reduces exposure while the real patch rolls out. This page says exactly where each control helps and where it doesn't. A good security engineer knows where a control stops working.
| Attack element | CVE | WAF control | WAF coverage | Server-side control in this repo | Residual risk |
|---|---|---|---|---|---|
PHPSESSID + PHP_SESSION_UPLOAD_PROGRESS request pattern |
109619 | Rule 20 PHP2Shell-Signature + rule 30 PHP2Shell_BODY (PHP rule set Version_2.3) |
Full, within the inspected part of the body | session.upload_progress.enabled=0 (php.ini and php_admin_flag), pm.max_requests=1 |
Field name past the inspection limit (body > 8 KB on ALB) on the upload path |
| Stale global state reused by the next request | 109619 | none (WAF can't see worker memory) | None | pm.max_requests=1 recycles every worker; patched PHP 8.4.27 |
None once patched |
| Multipart uploads | both | Rule 60 Block-Multipart-FormData outside /upload.php |
Conditional: full on paths that never take uploads | nginx only allows large bodies on /upload.php |
/upload.php must accept multipart, so it relies on rule 20/30 + patching |
| Oversized bodies | 109618 | Rule 70 Block-Oversize-Body (> 8 KB, OversizeHandling: MATCH) |
Partial | post_max_size=32M, nginx client_max_body_size |
Header amplification: many small part headers fit under 8 KB but expand in memory |
| PHP running out of memory mid-request | 109618 | none | None | memory_limit=-1 + container cgroup limit (--memory 512m), never RLIMIT_AS/RLIMIT_DATA |
None once patched |
| Repeated exploitation attempts | both | Rule 10 RateLimit-Posts (100 POST / 60 s / IP) |
Partial | pm.max_requests=1 |
One request can be enough; distributed sources evade per-IP limits |
fatal_error_backtraces (PHP 8.5 config) |
109618 | none (server config) | None | Documented: set 0 on PHP 8.5; not set on 8.4 |
Operator error on 8.5 hosts |
| Invalid / deprecated INI diagnostics | 109618 | none | None | Hardening file sets only valid directives; log_errors=On to surface warnings |
Legacy directives in other apps |
Per-request config overrides (.htaccess, PHP_VALUE) |
both | none | None | php_admin_* in the FPM pool; nginx passes no PHP_VALUE |
— |
Exemption bypass via path tricks (/upload.php/../x.php, %2f) |
— | Rules 60/70 match the upload path EXACTLY after URL_DECODE + NORMALIZE_PATH |
Full | nginx serves only exact .php files |
— |
| Resource | Default body inspection | Max | Notes |
|---|---|---|---|
| Application Load Balancer (this lab) | 8 KB | 8 KB | Can't be raised, so rule 70 is required |
| AWS AppSync | 8 KB | 8 KB | |
| Amazon CloudFront | 16 KB | 64 KB | association_config → request_body → CLOUDFRONT → KB_64 |
| Amazon API Gateway, Cognito, App Runner, Verified Access | 16 KB | 64 KB |
Moving this design behind CloudFront would raise the limit to 64 KB, so rule 20 could see further into upload bodies.
- Patch PHP (8.5.12 / 8.4.27 / 8.3.36 / 8.2.35). That's the only complete fix, so
variables.tfrefuses to deploy an unpatchedphp_image. - Keep
session.upload_progress.enabled=0after patching. - Keep or remove the WAF rules after patching depending on whether anything still needs the protection. Rule 60 is a good permanent control on apps that never take uploads.