Nuxt.jsのVuexはStateがユーザー間で共有される恐れがあるので注意が必要
一発目からややネガティブな記事で申し訳ないですが、ここ最近で一番衝撃だったので書きます。公式ドキュメントにも注意書きがあるので、ちゃんと読めと言われればそれで終わりなのですが、僕と同じように見落としている人がいないとも限らないので。
タイトルの通りなのですが、Nuxt.jsのVuexはStateがユーザー間で共有される恐れがあります。現在業務でECサイトを作っていますが、他人のページにアクセスなんて出来てしまったら、障害どころの騒ぎではありません。購入履歴が丸見えなのはもちろん、下手したら他人のアカウントで購入し放題です。しかし、Nuxt.jsでは一見普通の書き方でこれが発生します。早速、Nuxt.jsでVuexのstateを初期化してみましょう。
// store/state/index.js
export const state = {
hoge: '',
fuga: 0
}
一見何の変哲もないコードですね。ですが、これを書いた瞬間、すでにあなたは重大なセキュリティインシデントを引き起こしているかもしれません。仮に、ユーザーAがfugaに1を書き込んだとします。この後ユーザーBがアクセスしたとき、fugaにはどんな値が入っているでしょうか。多くの人はfugaに0が入っていることを期待するでしょう。しかし恐ろしいことに、この時fugaに1が入ってしまっていることがあります。もしこのfugaが個人情報だった場合、これはれっきとした情報漏えいです。
ここで、改めてNuxt.jsの公式ドキュメントを見てみましょう。よくよく読むと書いてあります。
モードに関わらず、サーバーサイドで不要な共有状態を避けるため、state の値は常に function でなければなりません。
そもそも、タイトルに書いたユーザー間で共有される現象は、サーバーサイドでデータが共有されることで起こります。対策としては、初期値ではなく初期値を返す関数をエクスポートする必要があります。先ほどのコードを直してみましょう。
// store/state/index.js
export const state = () => ({
hoge: '',
fuga: 0
})
こう書くことで、Stateがユーザー間で共有されることは無くなります。めでたしめでたし。
最後に、なぜこのような現象が起こるのか軽く考察してみたいと思います。サーバーサイドだと、アプリケーションはリクエスト毎には起動しないため、モジュールでエクスポートされた値はユーザー間で共有されます。そのため、何かの拍子にエクスポートされているオブジェクトが書き換えられると、それがそのまま他のリクエストで使われてしまうのだと思います。Nuxt.jsのVuexに限らず、サーバーサイドレンダリングではモジュールでエクスポートされた値がリクエスト間で共有され得ることを把握しておくべきでしょう。