
AWS セキュリティ速報(2026-143-AWS)と AWS Security Blog で紹介された PHP2Shell を緩和する AWS WAF ルールを試してみた
はじめに
PHP の 2 件の脆弱性(通称 PHP2Shell。CVE-2026-109618、CVE-2026-109619)に対し、AWS セキュリティ速報 2026-143-AWS では、影響を受ける PHP のバージョンと修正版の情報のほか、すぐに更新できない場合の暫定対処として AWS WAF のルールが案内されています。
また、AWS Security Blog でも、AWS WAF を使った緩和策が紹介されています。
今回は、PHP2Shell の緩和策として紹介されている 2 つのルール(PHP2Shell、Block-Oversize-Body)を検証用の ALB に入れ、curl で次の 3 点を確かめた結果を紹介します。
- 止まるリクエストと通るリクエストの違い
- PHP_SESSION_UPLOAD_PROGRESS がボディの先頭 8 KB より後ろにあるときの結果
- 攻撃ではないリクエストが止まる条件
AWS が案内する 2 つのルール
AWS セキュリティ速報は、AWS WAF を使っている場合の暫定対処として、CVE ごとに 1 つずつルールを挙げています。CVE-2026-109619 向けがカスタムルール PHP2Shell、CVE-2026-109618 向けが AWS マネージドルールのコアルールセット(CRS)に含まれる SizeRestrictions_BODY です。
PHP2Shell
速報に載っているルール定義です。
{
"Name": "PHP2Shell",
"Priority": 0,
"Statement": {
"AndStatement": {
"Statements": [
{
"ByteMatchStatement": {
"SearchString": "POST",
"FieldToMatch": { "Method": {} },
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }],
"PositionalConstraint": "EXACTLY"
}
},
{
"OrStatement": {
"Statements": [
{
"ByteMatchStatement": {
"SearchString": "PHPSESSID",
"FieldToMatch": {
"Cookies": {
"MatchPattern": { "All": {} },
"MatchScope": "KEY",
"OversizeHandling": "CONTINUE"
}
},
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }],
"PositionalConstraint": "EXACTLY"
}
},
{
"ByteMatchStatement": {
"SearchString": "PHPSESSID",
"FieldToMatch": { "Body": { "OversizeHandling": "CONTINUE" } },
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }],
"PositionalConstraint": "CONTAINS"
}
}
]
}
},
{
"ByteMatchStatement": {
"SearchString": "PHP_SESSION_UPLOAD_PROGRESS",
"FieldToMatch": { "Body": { "OversizeHandling": "CONTINUE" } },
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }],
"PositionalConstraint": "CONTAINS"
}
}
]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "PHP2Shell"
}
}
次の 3 つをすべて満たすリクエストを Block します。
- メソッドが POST
- Cookie のキーが PHPSESSID に完全一致する、またはボディに PHPSESSID が含まれる
- ボディに PHP_SESSION_UPLOAD_PROGRESS が含まれる
定義から、次のことが読み取れます。
- 文字列は大文字小文字を区別して比較します(TextTransformations が NONE)
- Content-Type の条件はありません
- ボディの OversizeHandling は CONTINUE です。WAF が検査できる範囲(ALB では先頭 8 KB)だけを見て判定します
Block-Oversize-Body
Block-Oversize-Body は、ボディが 8,192 バイト(8 KB)を超えるリクエストを Block するルールで、見るのはボディのサイズだけです。AWS Security Blog にカスタムルールとして載っています。
AWS セキュリティ速報では、同じ働きをする CRS の SizeRestrictions_BODY が紹介されています。
WCU(CRS の 700)の節約、カスタマイズ性、ログの追いやすさの点で、カスタムルール(Block-Oversize-Body)のほうが優れます。
すでに CRS(AWSManagedRulesCommonRuleSet)を使っている場合は、SizeRestrictions_BODY が同じ働きをすると、AWS Security Blog にも書かれています。
検証用のテンプレートには、このカスタムルールを入れています。Blog 上の名前は Block-Oversize-Body、テンプレート上の名前は block-oversize-body です。次の JSON は、テンプレートのルールから 403 のカスタムレスポンス(ルール名を返す設定)を除いたものです。名前と優先度はテンプレートと同じ値です。メトリクス名は、テンプレートでは先頭に WebACL 名が付きます。
{
"Name": "block-oversize-body",
"Priority": 5,
"Statement": {
"SizeConstraintStatement": {
"FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } },
"ComparisonOperator": "GT",
"Size": 8192,
"TextTransformations": [{ "Priority": 0, "Type": "NONE" }]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "block-oversize-body"
}
}
PHP2Shell と違い、ボディの OversizeHandling は MATCH です。AWS Security Blog は、検査の上限を超えるボディも対象にするための指定だと説明しています。
テンプレートの前の版(コミット cd6bafd)では、優先度 5 に CRS の SizeRestrictions_BODY を入れていました。block-oversize-body に替えた今の版でも、同じ形のリクエストに対する一致と、8 KB の境界で止めるルールが切り替わる位置は、前の版と同じでした。
WAF ログの記録は、前の版では 2 か所に分かれていました。
- 前の版(CRS の SizeRestrictions_BODY)
- WebACL に付けたルール名 aws-crs-size-restrictions-body が、nonTerminatingMatchingRules(Count 構成)または terminatingRuleId(Block 構成)に出ていました
- マネージドルールグループの中のルール ID SizeRestrictions_BODY は、ruleGroupList に出ていました。Count 構成では、リクエストが 200 で通っているのに、ruleGroupList の terminatingRule の action は BLOCK と記録されました
- block-oversize-body
- ルール名が nonTerminatingMatchingRules(Count 構成)または terminatingRuleId(Block 構成)の 1 か所に出ます。action はリクエストの結果と一致し、Count 構成なら COUNT、Block 構成なら BLOCK です。Block のときは responseCodeSent に 403 も記録されます
検証環境
この検証環境は CloudFormation テンプレートにしてあり、GitHub のリポジトリ(コミット febe2c7)で再現できます。
テンプレートは、上の 2 つのルールを含む WebACL を新しく作り、LoadBalancerArn で指定した ALB に関連付けます。本文に出てくる主なパラメータは次のとおりです。
- BlockPhpUploadProgress=true(既定値)で PHP2Shell が入ります。名前は block-php-upload-progress、優先度は 4 です。アクションは RuleAction に従い、Block のときは 403 でルール名を返します。それ以外は AWS セキュリティ速報の定義と同じです
- BlockLargeBody=true(既定値は false)で block-oversize-body が入ります
- RuleAction=Count にすると、WebACL のルールがすべて Count になります。Count は、一致を WAF ログとメトリクスに記録し、リクエストは通す設定です。Block へ切り替える前に影響を見るときに使います
デプロイのコマンド(RuleAction=Count で入れる例)
README のデプロイ例に、BlockLargeBody=true の 1 行を加えたものです。検証用の ALB を前提にしています。ALB に別の WebACL が付いている場合、関連付けはこの WebACL に置き換わります。AllowedCountries=JP は、日本以外からの POST を止める国別のルールの設定です。テンプレートにはレート制限のルールも入ります。RuleAction を Block へ切り替える前に、これらの影響も確かめてください。
aws cloudformation deploy \
--stack-name waf-post-guard \
--template-file template.yaml \
--region ap-northeast-1 \
--parameter-overrides \
AllowedCountries=JP \
RuleAction=Count \
BlockLargeBody=true \
LoadBalancerArn=arn:aws:elasticloadbalancing:REGION:ACCOUNT_ID:loadbalancer/app/NAME/ID
ALB は東京リージョンにあります。HTTPS リスナーに自己署名証明書を使っているので、curl には -k を付けています。以下の結果は、特に書かない限り RuleAction=Block、BlockPhpUploadProgress=true、BlockLargeBody=true の構成です(デプロイ例の RuleAction は Count です)。どのルールが止めたかは、403 レスポンスの x-waf-blocked-by ヘッダ(ルール名が入ります)と WAF ログで確かめました。各リクエストは 1 回ずつ送りました。
PHP2Shell の条件を curl で確かめる
PHP2Shell の 3 条件を満たす POST と、1 つだけ外した POST を curl で送り、レスポンスコードを見ました。
# Cookie の PHPSESSID + ボディの PHP_SESSION_UPLOAD_PROGRESS
curl -sk -o /dev/null -w '%{http_code}\n' -X POST "https://<ALB の DNS 名>/" \
-b "PHPSESSID=dummy" \
-F "PHP_SESSION_UPLOAD_PROGRESS=dummy" \
-F "a=b"
# 両方ともボディのフィールド
curl -sk -o /dev/null -w '%{http_code}\n' -X POST "https://<ALB の DNS 名>/" \
-F "PHPSESSID=dummy" \
-F "PHP_SESSION_UPLOAD_PROGRESS=dummy" \
-F "a=b"
| リクエスト | 結果 |
|---|---|
| POST、Cookie の PHPSESSID、ボディに PHP_SESSION_UPLOAD_PROGRESS | 403 |
| POST、ボディに PHPSESSID と PHP_SESSION_UPLOAD_PROGRESS | 403 |
| POST、Cookie が複数(a=1; PHPSESSID=x)、ボディに PHP_SESSION_UPLOAD_PROGRESS | 403 |
| POST、ボディに PHP_SESSION_UPLOAD_PROGRESS だけ | 200 |
| POST、Cookie の PHPSESSID だけ | 200 |
| PUT、ボディに PHPSESSID と PHP_SESSION_UPLOAD_PROGRESS | 200 |
-F の代わりに --data-binary で multipart のボディを手で組んだ POST も、1 つ目のコマンドと同じく 403 になりました。
PHP2Shell が検査するのはボディの先頭 8 KB まで
許可されたリクエストは、検査していない部分も含めて全体がそのまま ALB の背後へ送られます。
block-oversize-body を外した構成(BlockLargeBody=false、RuleAction=Block)で、multipart の POST を送りました。ボディは、大きな pad フィールドの後ろに PHPSESSID と PHP_SESSION_UPLOAD_PROGRESS(値はどちらも dummy)を置いた形で、PHP_SESSION_UPLOAD_PROGRESS の位置だけを変えています。表の値は、ボディ先頭を 0 としたときの PHP_SESSION_UPLOAD_PROGRESS の終端位置です。
| PHP_SESSION_UPLOAD_PROGRESS の終端位置(バイト) | PHP2Shell だけの結果 |
|---|---|
| 8,183 | 403 |
| 8,191 | 403 |
| 8,193 | 200 |
| 8,237 | 200 |
PHP_SESSION_UPLOAD_PROGRESS が先頭 8,192 バイトの内側に収まる POST は 403、収まらない POST は 200 でした。
block-oversize-body も Block にした構成(BlockLargeBody=true)で、同じ形の POST をボディ全体のサイズを変えて送ると、止めたルールは 2 つの文字列の位置で分かれました。
| ボディ全体のサイズ(バイト) | 2 つの文字列の位置 | 止めたルール |
|---|---|---|
| 8,106、8,196、8,206 | 先頭 8,192 バイトの内側 | block-php-upload-progress(PHP2Shell) |
| 8,256〜9,206 | PHP_SESSION_UPLOAD_PROGRESS の終端が 8,192 バイトより後ろ | block-oversize-body |
8,196 バイトと 8,206 バイトの POST は block-oversize-body の条件(8,192 バイト超)も満たします。ただし、優先度 4 の block-php-upload-progress が優先度 5 の block-oversize-body より先に評価されて止めています。
公開されている PoC は後者の形です。ソースを読んだ限り(実行はしていません)、multipart のボディは、1,000 個を超えるダミーフィールドの後ろに PHPSESSID と PHP_SESSION_UPLOAD_PROGRESS を置く構造です。その後ろに、ファイルと巨大なフィールドが続きます。この順序を無害なダミーの値で再現した 1,056,388 バイトの POST(2 つの文字列はボディ先頭から約 56 KB の位置)を送ると、403 で止めたのは block-oversize-body でした。
攻撃ではないリクエストが止まる条件
8 KB を超える正当なリクエストも block-oversize-body で止まる
Count 構成(RuleAction=Count)で次の 5 種類を送ると、レスポンスはすべて 200 でした。WAF ログには、5 件とも block-oversize-body に一致したと記録されました(nonTerminatingMatchingRules)。
| リクエスト | ボディのサイズ |
|---|---|
| JSON の POST | 20,013 バイト |
| 画像アップロード(multipart の POST) | 102,710 バイト |
| PUT | 15,000 バイト |
| PATCH | 15,000 バイト |
| JSON ボディ付きの GET | 20,013 バイト |
RuleAction=Block にすると、これらは 403 で止まりました。
閾値(8,192 バイト)の前後のサイズと、WAF の検査範囲(先頭 8,192 バイト)を大きく超えるサイズのボディも、RuleAction=Block で確かめました。無害な urlencoded の POST を、ボディのサイズだけ変えて送った結果です。
| ボディのサイズ(バイト) | 結果 |
|---|---|
| 8,191 | 200 |
| 8,192 | 200 |
| 8,193 | 403 |
| 1,048,576 | 403 |
| 5,242,880 | 403 |
8,193 バイト以上の POST では、WAF ログの oversizeFields に REQUEST_BODY が記録されました。検査された範囲(requestBodySizeInspectedByWAF)は 8,192 バイトです。
JSON や text/plain のボディでも PHP2Shell で止まる
次の 2 つの POST は、どちらも 403 になりました。
- Content-Type が application/json、ボディが
{"PHPSESSID":"dummy","PHP_SESSION_UPLOAD_PROGRESS":"dummy"} - Content-Type が text/plain、ボディが
PHPSESSID=dummy PHP_SESSION_UPLOAD_PROGRESS=dummy
たとえば PHP の解説記事を投稿するフォームで、本文にこの 2 つの文字列が含まれていると、その POST も止まります。
AWS セキュリティ速報のルールに「Content-Type ヘッダに multipart/form-data を含む」条件を AND で足したルールも試しました。このルールは、Content-Type を小文字にして比較します。Count で WebACL に追加し、multipart の POST と上の 2 つの POST を送ると、このルールは multipart の POST に一致し、JSON と text/plain の POST には一致しませんでした。Content-Type を指定せずに --data-binary で送った POST(curl が application/x-www-form-urlencoded を付けます)にも一致しませんでした。このルールは、速報の定義より止める範囲が狭くなります。小文字にしない(大文字小文字を区別する)ルールは、Content-Type が Multipart/Form-Data の POST に一致しませんでした。
テンプレートの block-multipart-post を有効にした場合
テンプレートには block-multipart-post というルールも入っています(BlockMultipartPost=true で有効。既定は無効)。Content-Type に multipart/form-data を含む POST を、サイズや中身を問わず止めるルールです。AWS Security Blog に載っている Block-Multipart-FormDataに、POST の条件を加えたものです。正当なファイルアップロードや multipart のフォーム送信も止まります。
このルールを有効にしたとき、block-php-upload-progress(PHP2Shell)と block-oversize-body がどう重なるかを見ました。BlockMultipartPost=true、RuleAction=Count で 3 種類の multipart POST を送り、WAF ログに記録された一致を表にしています(— は一致なしです)。
| リクエスト | block-multipart-post | block-php-upload-progress(PHP2Shell) | block-oversize-body |
|---|---|---|---|
| multipart POST、Cookie の PHPSESSID、ボディに PHP_SESSION_UPLOAD_PROGRESS | 一致 | 一致 | — |
| 通常の multipart POST | 一致 | — | — |
| 上の PoC の順序を再現した multipart POST(1,056,388 バイト) | 一致 | — | 一致 |
block-multipart-post は block-php-upload-progress と block-oversize-body より先に評価されます。RuleAction=Block で有効にすると、multipart の POST はここで止まり、後ろのルールまで評価が進みません。表に複数の一致があるのは、Count では評価が続いたためです。無効にした場合、2 つの文字列を含まない通常の multipart POST は、ボディが 8,192 バイト以下なら通ります。8 KB を超えるもの(102,710 バイトの画像アップロードなど)は、block-oversize-body が止めます。
まとめ
AWS セキュリティ速報 2026-143-AWS と AWS Security Blog は、PHP の 2 件の脆弱性への暫定対処として AWS WAF のルールを紹介しています。そのうち PHP2Shell と Block-Oversize-Body を ALB で検証しました。
AWS WAF による緩和としてこの 2 つのルールの採用を検討している場合は、今回の評価結果を参考にお試しいただければと思います。
ただし、WAF のルールは暫定対処で、根本的な解決は修正版(8.5.12、8.4.27、8.3.36、8.2.35)への PHP の更新である点にご留意ください。
次の AWS Security Blog の記事には、AWS WAF のルールと PHP 側の設定による対処がまとまっています。あわせて参照いただくことをおすすめします。
Amazon Threat Intelligence が脆弱性の公開前に悪用を確認した経緯と、推奨される対応は、次の記事にあります。












