CakePHP 3 のテンプレートで FormHelper を使わずに、直接 CSRF トークンを埋め込む
はじめに
CakePHP 3 には FormHelper という仕組みがあります。これを用いると Form 関連のタグの記述量が減ったりして便利なのですが、タグが見えづらくなるデメリットもあります。
これを使わずに、テンプレートに直接 <form> などを記述すると、POST送信した際に「CSRF token mismatch.」のエラーが出ます。
だからといって CSRF 保護をやめてしまうわけにもいきません。
そこで今日は FormHelper を使用せずに、直接 CSRF トークンを埋め込む方法をご紹介します。
今回使用した CakePHP のバージョンは 3.8.4 です。
1. 実装方法
実装はとても簡単で、<form> 内に下記1行を追記するだけでOKです。
src/Template/sample.ctp
<form method="post">
<!-- 省略 -->
<input type="hidden" name="_csrfToken" value="<?= $this->request->getParam('_csrfToken') ?>">
</form>
- The W3C Markup Validation Service
- https://validator.w3.org/
2. Javascript で自動挿入
上記の方法だと、毎回 <input> を記述する必要があり、ちょっと手間に感じるかもしれません。そんな時は Javascript で下記のようなコードを用意すれば、自動的に挿入することもできます。
src/Template/Layout/default.ctp
<script>
// CSRFトークン
var CSRF_TOKEN = "<?= $this->request->getParam('_csrfToken') ?>";
window.onload = function() {
// 各 <form> で method="post" なら CSRFトークンの <input> を末尾に追加
var forms = document.getElementsByTagName("form");
var length = forms.length;
for (var i = 0; i < forms.length; i++) {
var form = forms[i];
if (form.getAttribute("method") !== "post") {
continue;
}
// CSRFトークンの <input> を生成
var inputCsrf = document.createElement("input");
inputCsrf.setAttribute("type", "hidden");
inputCsrf.setAttribute("name", "_csrfToken");
inputCsrf.value = CSRF_TOKEN;
// <form> に追加
form.appendChild(inputCsrf);
}
};
</script>
3. おわりに
CakePHP 3 の FormHelper を使うと色々と自動でやってくれるので便利ではあるものの、その部分がブラックボックスになり、把握するためのコストが生じます。
<form> などを直接テンプレートに記述する場合は CSRF 保護を無効化せずに、上記のような手段でトークンを追加するのがセキュアでいいと思います。