Edited at
CakePHPDay 7

[CakePHP3] データ漏洩していませんか?RequestHandlerの危険性

More than 1 year has passed since last update.

予定していたAdventCalendarの記事が間に合いそうになかったので

CakePHPのslackチャンネルで話題にもなったことを記事にしました:kissing_heart:

今回の検証バージョンはCakePHP3.5.7です。

CakePHP 3.1以降のバージョンで発生します。

API実装予定でないWebサービスの場合は特に気が付きにくいので確認してみてください!

今回指摘の箇所はまだリリースされていませんが改善予定です。

しかしこのPRだけではまだ危ないです。

https://github.com/cakephp/app/pull/569

[追記]公式のブログでも公開されました!

https://bakery.cakephp.org/2017/12/08/potential-information-disclosure-in-application-skeleton.html#


データ漏洩を体験

こんな感じのユーザ一覧ページを作成したいとします。

https://qiita.com/users


CakePHPインストール

cookbookに書いてるとおりにインストールします。

$ composer create-project --prefer-dist cakephp/app app


テーブル作成

usersテーブルを作成。

適当に5件ほどデータを挿入します。

CREATE TABLE users (

id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created DATETIME,
modified DATETIME
);


bake

bakeでModel/Controller/Templateを作成します。

$ bin/cake bake all users


テンプレート編集

ユーザ一覧画面でユーザ名だけ表示したいのでusername以外の項目を削ります


/src/Template/Users/index.ctp

    <table cellpadding="0" cellspacing="0">

<thead>
<tr>
<th scope="col"><?= $this->Paginator->sort('username') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?= h($user->username) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

ユーザ一覧が表示される画面ができます。

index


これで完成?

これであとはデザインを整えるだけ!とするとデータが漏洩してしまします。


データが漏洩しているところを確認してみる

実際にやってみます。

chromeのdevツール以下のようにしてajaxでリクエストを叩いてみます。

var jq = document.createElement('script');

jq.src = "http://code.jquery.com/jquery-3.2.1.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

$.ajax({

type: 'GET',
url: '/users',
dataType: 'json',
success: function(response){ console.log(response); },
error: function(req, err){ console.log(err); }
});

!!

テンプレートでは削除したはずのメールアドレスが表示されてしまいます。

chrome


原因

インストール時点でデフォルトでRequestHandlerコンポーネントが読み込まれています。

このRequestHandlerコンポーネントはAJAXリクエストを自動で判定して_serializeでセットした値をjsonにして返却します。


/src/Controller/AppController.php

    public function initialize()

{
$this->loadComponent('RequestHandler');
}

またAppControllerはリクエストを判定して$this->set()した値をすべて_serializeにしています。


/src/Controller/AppController.php

    public function beforeRender(Event $event)

{
// Note: These defaults are just to get started quickly with development
// and should not be used in production. You should instead set "_serialize"
// in each action as required.
if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml'])
) {
$this->set('_serialize', true);
}
}

そしてbakeで生成したメソッドも_serializeにセットしています。

また、ここの$this->set('_serialize', ['users']);を削除しても上記の影響でserializeされます。


/src/Controller/UsersController.php

    public function index()

{
$users = $this->paginate($this->Users);

$this->set(compact('users'));
$this->set('_serialize', ['users']);
}


以上のことからデフォルトのままだとAJAXリクエストをするときにEntityのデータがserializeされjsonで返却されてしまいます。


対策


対策1

RequestHandlerコンポーネントを使わないなら削除します。

とりあえずこれをやっておけば安心です。

これでAJAXリクエストを自動で判定して_serializeでセットした値をjsonにして返却されることはなくなります。


/src/Controller/AppController.php

// $this->loadComponent('RequestHandler');



対策2

RequestHandlerコンポーネントを使いたいときがあると思います。

その場合は以下のようにします。

自動で_serializeにセットされるコードを削除します。


/src/Controller/AppController.php

    public function beforeRender(Event $event)

{
// Note: These defaults are just to get started quickly with development
// and should not be used in production. You should instead set "_serialize"
// in each action as required.
// if (!array_key_exists('_serialize', $this->viewVars) &&
// in_array($this->response->type(), ['application/json', 'application/xml'])
// ) {
// $this->set('_serialize', true);
// }
}

bakeで生成される$this->set('_serialize', ['users']);を削除する。

本当に_serializeしたい部分だけ記載するようにします。


/src/Controller/UsersController.php

    public function index()

{
$users = $this->paginate($this->Users);

$this->set(compact('users'));
// $this->set('_serialize', ['users']);
}



対策3

表示する場合の対策ですが...

表示しない項目はEntityで$_hidden設定することを心がけましょう。

bakeをするとpasswordカラムは自動で$_hiddenに設定されるため今回は表示されませんでした。

https://book.cakephp.org/3.0/ja/orm/entities.html#id15