他人様のコードレビューをして、念のためステージングで動作確認していたらパラメータが足りない。
error_logを確認すると、衝撃的なWarningが出てますた (´・_・`)
PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0, referer: http://hoge.com/fuga.php
操作
とある検索機能なのだが、いろいろ心配なので、ステージングで負荷試験を兼ね、動作検証を実施。
画面上にある検索条件を全て選択(チェックボックスには「全て選択」ボタンあり)して検索を実行し、動くかどうか、クエリは正しくビルドされるかを検証していました。
概要
POSTパラメータに1000個を超えるパラメータを渡した場合に、PHP Warning がでる。
1000個を超えたパラメータはプログラムに渡らない。
出典
PHPマニュアルの「max_input_vars」の項を参照。
PHPマニュアル
まずい点
引用
入力変数 を最大で何個まで受け付けるかを指定します (この制限は、スーパーグローバル
$_GET
、$_POST
そして$_COOKIE
にそれぞれ個別に適用されます)。 このディレクティブを使うと、ハッシュの衝突を悪用したサービス不能攻撃を受ける可能性を軽減できます。 このディレクティブで設定した数を超える入力変数があった場合はE_WARNING
が発生し、 それ以降の入力変数はリクエストから削除されます。
脆弱性
パラメータ数の上限をどんどんあげてしまうと、以下の処理順で負荷が増大し、脆弱性になります。
- GETやPOSTで渡された変数はハッシュ構造に格納されます
- 変数名に細工を加えることで、ハッシュの衝突が生じます
- 全てのハッシュが衝突した場合、n個の変数の格納処理には、O(n2)の時間がかかる
- CPUに大きな負荷をかけてしまう
結果、サーバに過度の負荷をかけ、サービスを稼動できない状態にすることが可能になってしまうのです。
過去に報告された脆弱性
対象のPHPバージョン
PHP5.3.9以前は上限が無い。
PHP5.3.10で上限が設けられ、デフォルト1000となっています。
現代の一般的なサーバーのCPUでは、1000程度であれば負荷増大にまで至らないので、問題ないと判断できます。
やっちゃいけない
上限数を php.ini などで上げる
上述の通り、脆弱にするだけなので、運用と設計を考え直すが吉。
以下はやるなよ?いいか、ぜっっっったいにやるなよ?
max_input_vars = 2000
リクエストボディ直接参照
POSTとしてプログラムでは受け取れないが、リクエストのボディから直接参照することができちゃいます。
GETと同じ形式で取得できるので、自力でパースすれば良いのですが、例えば parse_str
でパースしようとすると怒られます。
<?php
$hoge = file_get_contents( 'php://input' );
parse_str($hoge, $hoges);
PHP Warning: parse_str(): Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in ....
だからと言って・・・
foreachで回して取ってくるとか、自作しちゃだめですよ?
そもそも
パラメータ数が1000個の時点で、設計としておかしいです。
運用、設計の見直し、絞り込む仕組み、などを考慮し、「考えられる項目を闇雲に全部選択できる」ような思考停止な実装はしないようにしていきたいものです。
さすがにCのソースを追いかける時間はないので、今回はこの辺で。