下記のようなアプリケーションを考えます。
概要
- 入力フォームのあるWebアプリ
- バックエンドはPHP
- フロントエンドはVue.js
- 通信はAjax(フォームを描画するためのいくつかのパラメータのGETも、フォームに入力した内容のPOSTも)
流れ
- フロントのURLへアクセスし、バックエンドからいくつかのパラメータを取得(この際、CSRF対策用のトークンも発行)、フォームをVue.jsで描画 (※a)
- フォームで入力
- フォームの入力内容をaxiosでAjax送信
- バックエンドのPHPはAjax通信で送信されたパラメータをチェック (※b) し、実際の処理を行う
上記の内容のうち、
(※a) と
(※b) の部分(実際にサーバにアクセスする部分)でトークンを発行したりチェックしたりしています。
CSRF対策の処理については
とっても簡単なCSRF対策 – Qiitaを参考にさせて頂いています。
現象
上記の
(※a) と
(※b) でセッションをスタートさせているのですが、このセッションIDが異なる。
セッションIDが異なれば、
とっても簡単なCSRF対策 – Qiitaの判定は不一致になるのでコケます。
この原因は何だろうか、とずっと悩んでおりました。
原因
原因としては、やはりというかクロスオリジンでした。Vue.jsの開発環境ですとポート番号8080でアプリケーションが開かれますが、PHPは普通に80で待ち受けていました。
ポートが異なれば異なるオリジンと判定されるため、セッションの引継ぎが行われず、結果として不一致が起きていたということのようです。
そのため対策としてはCORSの設定をしてあげれば良いということになります。
対策
axiosでGETやPOSTする前にヘッダ情報を付与することでクリアできました(PHP側は予めヘッダを許可していたため)。
GET時
const axiosPost = axios.create({
xsrfHeaderName: 'X-CSRF-Token',
withCredentials: true
})
Promise.all([
axiosApi.get(this.api.hoge)
]).then(([result]) => {
//処理
})
//以下略
POST時
const axiosPost = axios.create({
xsrfHeaderName: 'X-CSRF-Token',
withCredentials: true
})
axiosPost.post(URI + 'api/hoge', this.senddata)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
こんな感じです。
……気付けば「ああそうか」ということではあるのですが、なかなか気付かず嵌まっておりました……。
参考
CSRF対策
axios
CORS