Cakephp PHP WEB開発

【Cakephp4】汎用性の高いCSRFトークンの発行方法: AJAX編

※本サイトはPR表記を含みます。

Cakephp4でCSRFトークンを汎用的に利用できる方法です。

WEB開発をする上でAJAX通信を利用することは多々あると思うので、参考にしてみてください。

CSRFトークンがない状態でPOSTリクエストを投げると以下のような例外になります。

 command
error: [Cake\Http\Exception\InvalidCsrfTokenException] CSRF token from either the request body or request headers did not match or is missing.

Cakephp4ではデフォルトで CsrfProtectionMiddleware が有効になっています。CSRF対策はセキュリティ観点上必須です。

解決策としては、リクエスト前にCSRFトークンをセットすればOK。ついでに複数のビューでCSRFトークンが必要になる場合に備えて、必要に応じてCSRFトークンを発行できる形にしておきます。

default.phpなど汎用性の高いレイアウトファイルにCSRFトークンを埋め込む

templates/layout/default.php

<?php 
// CSRFトークン出力
if (!empty($this->fetch('isCSRFToken'))) {
    echo $this->Form->hidden('_csrfToken', ['value' => $this->getRequest()->getAttribute('csrfToken')]);
}

isCSRFToken フラグを後に作成しますが、true だった場合にFormヘルパーを利用して、CSRFトークンを発行する形にします。

layout ディレクトリ以下のファイルはヘッダー・フッターなど共通化するテンプレートファイルを配置する場所です。そのファイルにCSRFトークンを仕込んでおけば、各ビューに読み込まれるので、必要な場合に応じてCSRFトークンを出力することができるようになります。

CSRFトークンが必要なビューに対しフラグを立て、CSRFトークンを出力させる

任意のビュー(テンプレート)ファイルに以下を記述してください。

// CSRFトークン発行
$this->assign('isCSRFToken', true);

isCSRFToken に true を仕込み、先ほどのレイアウトファイルに記述した、条件判定を実行される形にし、CSRFトークンが出力されるようにします。

Chromeなどの開発に利用しているブラウザをリロードし、Ctrl + U などでソースコードを表示してCSRFトークンが発行されているか確認します。

以下のようなインプットフィールドが出力されているはずです。

<input type="hidden" name="_csrfToken" value="CSRFとーくんだよ">

AJAXの ajaxSetup()にCSRFトークンを埋め込む

JSコードのAJAX通信処理をしている箇所の手前などに、ajaxSetup()を利用して、CSRFトークンを組み込みます。

var csrf = $('input[name="_csrfToken"]').val();
$.ajaxSetup({
    beforeSend: function(xhr) {
        xhr.setRequestHeader('X-CSRF-Token', csrf);
    }
});

これで、AJAXリクエスト時にCSRFトークンが一致しない、もしくはないといったエラーは回避できるようになると思います。(回避というか、セキュリティ上必ずトークンは必要なんですが)

FormProtectionが有効になっている場合はフォームチェックを無効にしておく

FormProtection はフォームの改ざんをチェックするためのコンポーネントです。このコンポーネントもデフォルト有効にしておくべき。
ですが、AJAX利用などでは、不要になるので特定のアクションに限り無効にしておきます。

use Cake\Event\EventInterface;

// クラス内に記述
public function beforeFilter(EventInterface $event)
{
    parent::beforeFilter($event);
    $this->FormProtection->setConfig('unlockedActions', ['edit']); // 無効にしたいアクション名をセット
}

これで、期待した通りにセキュリティも考慮されたリクエスト通信ができるようになると思います。

■ Cakephp4: 特定のアクションのためにフォームの改ざんを無効にする
https://book.cakephp.org/4/ja/controllers/components/form-protection.html#id5

-Cakephp, PHP, WEB開発
-