Web Storage (localStorage, sessionStorage)にセッション情報を置くのをやめよう


シーエー・アドバンス Advent Calendar 2018 2日目担当の @togana です。

さて、本題。

みなさん、Web Storage 使ってますか？
僕は使っていました。
そうタイトル通り Web Storage にセッション情報をおいていました。
なぜそうしたのかって？簡単です。巷に溢れているチュートリアルやサンプル記事がそうなっているのです。

こういう言い方をするとなんでだめなの？ということになると思うので、そこらへんの話を書こうと思います。

※必ずしもここに書いてあることが正しいとは限らないので、そういう意見もあるよぐらいの気持ちで参考にしてください。

※書き終わった後に気づいたんですけど、過去にJWTについては同じようなこと書いてる人がいて、反論してる人もいました。そしてまとめられてました！！！僕は大丈夫！ぽかしない！って方はそもそもこの記事読む必要ないですが、そんなことが起きる可能性があるって知っておいてほしいので投稿することにしました。
参考: http://twitmatome.bogus.jp/archives/28467

# Web Storage とは

まずは、Web Storage がなにかについて説明します。

MDN web docsを参照することにしましょう。
参照: https://developer.mozilla.org/ja/docs/Web/API/Web_Storage_API

> Web Storage には、以下の 2 種類の仕組みがあります
>
>  sessionStorage は、ページのセッション中 (ページの再読み込みや復元を含む、ブラウザを開いている間) に使用可能な、生成元ごとに区切られた保存領域を管理します。
>  localStorage も同様ですが、こちらはブラウザを閉じたり再び開いたりしても持続します。

と記載されています。

よくサンプルに出てくるのは localStorage においている例ですが、そもそも session と名のつく領域があるにもかかわらず local を利用しているのに疑問が出てきますね。

そして、名前的にはまさに！って思う sessionStorage も意訳すると、ページのセッション中に使用可能な(**サーバが関与するようなセッションじゃないです！！！！**)、生成元ごとに(ドメインごと)区切られた保存領域を管理します。

**今回話したいことは、まさにこれだけです。**

そもそも、Web Storage はクライアント側のセッションを管理するために存在するのです。

**サーバ側とのやり取りのセッションを管理するなんてだめ絶対**

ちなみに仕様書にある記載には、Cookie では解決できない下記の2点を解決するためと記載されています。
参照: https://html.spec.whatwg.org/multipage/webstorage.html#introduction-15

意訳:

- 異なるウィンドウで複数のトランザクションを同時に実行することを想定
- 複数のウィンドウにまたがり、現在のセッションが終了しても存続するストレージを想定

そもそも、Web Storage はクライアント側のセッションを管理するために存在するのです。(2回目)

**サーバ側とのやり取りのセッションを管理するなんてだめ絶対(2回目)**

# どういう問題が起きるか

## 外部ドメインからの参照ができません

仕様的な問題です。

そこまで問題にはならないと思いますが、セッション情報を入れても他のドメインから読めないのです。
世の中の人達は BFF とか使ってるから大丈夫って人もいるかと思いますが、そうじゃない場合もあると思うので記載しました。

## JavaScript を利用しないと書き込み参照ができません

仕様的な問題です。

これは、基本的に現代において問題になることはないと思いますが、他の問題につながるので記載しました。

JavaScript からしかアクセスできないということは XSS がサイトに存在した場合、 Web Storage の中身が見られたい放題です。
これが、Cookie の場合は JavaScript からアクセスできないようにする HttpOnly属性 があるのでまだ安心です。

**この問題があるので web Storage 内部にセッション情報を置くのはやめようにつながるわけです。**

## JWT だと改ざん検知とかできるから大丈夫。じゃない。

最後はよくあるサンプルのやってしまってる問題です。

JWT でできるのは改ざんの検知だけです。XSS を利用してセッションIDを取得された場合、そのまま乗っ取られます。
2つ目と同じ結論になるんですが、XSS が存在しなければ大丈夫って話なんですが、乗っ取られた後のことを考えてみましょう。

### 乗っ取られたその後

JWT は基本的にサーバ側で発行して exp をクレームセット内に埋め込んで発行されます。
改ざん検知ができるので exp を確認することで認証が切れたか判定することができるわけです。

とてもお手軽で、サーバサイドは発行して改ざんされていないか検知すれば ok なのです。

※よくみるサンプルとかはそうなっていることが多い気がします。
※サーバ側で jti を保持して利用可能か判定を行うようにすれば安全と言い出す方もいるかも知れないが、 JWT を使うメリット全然なくなるので使わないほうがよいと思います。

**要するにサーバ側で発行した後は削除できないのです。
削除できないセッション情報が漏れたらどうなるか。
簡単ですね！期限が切れるまでやりたい放題です！**

# ではどうするのか？

Cookie を利用します。
参照: https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies

> サーバーがユーザーのウェブブラウザーに送信する小さなデータであり、ブラウザーに保存されて次のリクエストと共に同じサーバーへ返送されます。一般的には、二つのリクエストが同じブラウザーから送信されたものであるかを知るために使用されます。

とういわけで Cookie は Web Storage とは違い、サーバサイドのセッション管理を行う用途で作られています。

そして、こういう記載もあります。

> Cookie は、クライアント側の汎用的な記憶領域として使用されたことがあります。これは他にクライアントへデータを保存する手段がなかった頃は合理的でしたが、現在では新しいストレージ API を採用することが推奨されます。

クライアントサイドの記憶領域としては Cookie の利用は非推奨です。散々使うなと言った Web Storage の出番です。

# しかし Cookie には...

参照: https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies#Security

> HTTP Cookie は仕組み全体が本質的に安全ではないため、機密情報や重要な情報を保存したり転送したりしてはいけません。

機密情報や重要な情報ではなくセッションIDを格納しましょう。
そして、長い歴史があるので問題に対する答えが用意されています。参考にしていきましょう。

- https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies#Session_hijacking_and_XSS
- https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies#Cross-site_request_forgery_(CSRF)

# まとめ

Web Storage にはサーバ側のセッション情報は入れてはいけません。クライアント側のセッションの管理を行うものです。

じゃあ使い所はどこなのか。

> ページのセッション中 (ページの再読み込みや復元を含む、ブラウザを開いている間) に使用可能な、生成元ごとに区切られた保存領域

**なるほど、キャッシュだ！！！**

セッション管理に web Storage を利用する場合は、作られた意図と違う用途で用いることになるので覚悟を持って使いましょう！