Code Day's Night

ichikawayのブログ

CakePHPではDBカラムのSQLインジェクションに注意!

CakePHPアドベントカレンダー12日目の記事です。

市川@cakephperです。ちゃんとCakePHP使ってますよ!
丹精込めてVAddyを作ってます。
最近コンビニで常陸野ネストビール セッションIPAが売っててテンションが上がりますね。

CakePHP2,3のDBカラム名の扱い

CakePHP2の公式ドキュメントでは下記の注意書きがあります。

CakePHP は、配列の値部分のみエスケープします。決して キーにユーザーデータをセットしないでください。SQLインジェクションの脆弱性になります。

CakePHPのORMでWHERE句を生成する場合に、カラム名やSQLスニペットはエスケープされません。
あまりないパターンかもしれませんが、ユーザから送信されたリクエストデータの配列をそのまま条件に指定している場合はSQLインジェクションにつながります。

CakePHP3 公式ドキュメントでも同じく注意書きがあります。(該当箇所はリンク先の下の方の赤い枠で囲まれた箇所です)

具体例

例えば何かしらの検索条件のフォームがあったとして、ユーザが条件を選択して送信した
$this->request->data['Model']

['type' => 1, 'keyword' => 'あいうえお'];

であった場合、この送信された配列データをそのまま下記のようにfindメソッドの条件に渡している場合は危険です。

$this->Model->find('all', ['conditions' => $this->request->data['Model']]);

このように送信されたデータが連想配列になっていて、フォームの検索項目を増やしていけば自然と検索条件も増えていくため一見便利なように見えますが、配列のキーは何もエスケープされずにWHERE句のカラム名として使われます。
受け取ったリクエストデータの配列のキーが改ざんされた場合はSQLインジェクションの脆弱性が発生します。

CakePHP3の場合は、where()メソッドに渡す配列のキーがエスケープされずそのまま渡されますので同様に注意が必要です。

対応策

ユーザからのリクエストデータが配列の場合は、キーを信用せず値だけ使うようにしましょう。

$condition['type'] = $this->request->data('Model.type');
$condition['keyword'] = $this->request->data('Model.keyword');

追記(2017/12/26)

CakePHP2の、ControllerクラスのpostConditions()にこのSQLインジェクションの脆弱性があり、2017/12/16に修正されています。
Securityコンポーネントを使わずに、postConditions()だけを使ってfindの条件(conditions)を作成してfind()に渡している場合は危険ですのでご注意ください。
Make postConditions() less permissive. by markstory · Pull Request #11526 · cakephp/cakephp · GitHub

基本的には信用するフィールドのみ受け付けるか、最新のCakePHP2を使うか、私が作っているSecurePHPライブラリのように配列のキーで受け付ける文字列を制限してNGのものは削除するのが良いかと思います。 GitHub - ichikaway/securephp: SecurePHP is the library for security