LoginSignup
94
68

More than 3 years have passed since last update.

SPA認証トークン、Cookieに保存するか?LocalStorageに保存するか?

Last updated at Posted at 2019-12-24

この記事は CBcloud Advent Calendar 2019 19日目の記事です。

CBcloud 徳盛です。
HTML5のLocal Storageを使ってはいけない(翻訳)やAuth0のドキュメント内のWhere to Store Tokens
に記載されている通り、LocalStorageに保存するのはアンチパターンだと認識してはいますが、その理由を自分の言葉で説明しろと言われると無理だったので調べてみました。

CORS時のCookieの挙動をある程度理解する

まずCookieとは

サーバがブラウザに送信する小さなデータです。この情報はブラウザに保存され、次のリクエスト時に保存されたデータがサーバへ送信されます。
詳しくはこの辺りを参照してください。
・HTTP Cookie - HTTP | MDN - Mozilla
・HTTP Cookieとは:超入門HTTP Cookie - @IT

挙動を調査 (SPAで他のドメインのAPI呼び出すときとか)

詳細はオリジン間リソース共有 (CORS) - HTTP | MDNに載っているので参照してください。

気をつける点としては以下の4つかと思います。

  • リクエスト送信時にwithCredentials=trueをしないとset-cookie情報があってもブラウザがCookieをセットしてくれない
  • withCredentials=trueをしても、サーバ側でAccess-Controll-Allow-Credentials=trueをレスポンスヘッダにセットしないと、ブラウザがレスポンスを処理してくれない。ただし、Cookie情報は保存される。
  • Access-Control-Allow-Originヘッダーにワイルドカードを指定できない
  • ブラウザの設定でサードパーティCookieを拒否している場合、set-cookie情報があっても保存されない(Safariはデフォルトブロック)

ちなみにCookieを確認する際は、Chrome拡張等のCookie参照ツールを使うのをお勧めします。はじめChromeのdevtoolで確認していましたが、xhrの実行元ドメインに登録されるのかと勘違いしていて、気づくのに時間を要してしまいました。そりゃそうですよね。。
image.png

念の為、LocalStorageとは

Web Storageの一種で、Cookieよりも直感的にキーバリューの値をブラウザに保管することができます。
JavaScriptを利用して更新や取得をおこないます。

それぞれのリスクについて

リスクに関連する両者の大きな挙動の違いとしては、javascriptからのアクセスを拒否できるかどうかと勝手に送信してしまうかどうかです。

type jsからのアクセス 自動送信
Cookie 不可(http-only属性による) する
LocalStorage 常に可能 しない

Cookieはhttp-only属性を利用することでjsからのアクセスを不可にすることが可能ですが、LocalStorageはそもそもjsで利用する前提のため当然できません。
また、Cookieはドメインが一致していれば自動でアクセス先にデータを送信してしまいますが、LocalStorageは送信しません。
これらの違いを踏まえ、CSRFとXSSの脆弱性について考えていきたいと思います。
3分でわかるXSSとCSRFの違い

Cookieに保存した場合のリスク

自動で送信してしまうCookieの挙動を利用した攻撃に、クロスサイトリクエストフォージェリ(CSRF)があります。

クロスサイトリクエストフォージェリ(CSRF)とは、Webアプリケーションに存在する脆弱性、もしくはその脆弱性を利用した攻撃方法のことです。 掲示板や問い合わせフォームなどを処理するWebアプリケーションが、本来拒否すべき他サイトからのリクエストを受信し処理してしまいます。
クロスサイトリクエストフォージェリ(CSRF) | トレンドマイクロ

セッションの保存先をCookieにしたSPA実装時のAPIサーバ側でCSRFを防ぐためには、CORSのAccess-Control-Allow-Originを正しく設定しただけでは不十分です。
あくまでブラウザがレスポンスをブロックするだけで、サーバ側で処理自体はされてしまうためです。

簡単な対応策として、独自ヘッダを通信時に付与することでプリフライトリクエストを強制的に発生させることで、そのレスポンスをブラウザにブロックさせ、その後のメイン処理をさせない手法があります。
ただし、推奨はされないとのこと(徳丸さんのtwitter下記参照)。

この辺りはまだ何故推奨されないのか理解できていないため、今後調査していきます。

ちなみにプリフライトリクエストは以下の内容を参照してください。

「単純リクエスト」 (前述) とは異なり、「プリフライト」リクエストは始めに OPTIONS メソッドによる HTTP リクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうかを確かめます。サイト間リクエストがユーザーデータに影響を与える可能性があるような場合に、このようにプリフライトを行います。
オリジン間リソース共有 (CORS) - HTTP | MDN

LocalStorageに保存した場合のリスク

LocalStorageはJavaScriptからアクセスが可能なため、XSSを利用したセッションハイジャックを考慮する必要があります。

クロスサイトスクリプティングとは、ユーザのアクセス時に表示内容が生成される「動的Webページ」の脆弱性、もしくはその脆弱性を利用した攻撃方法のことです。動的Webページの表示内容生成処理の際、Webページに任意のスクリプトが紛れ込み、Webサイトを閲覧したユーザ環境で紛れ込んだスクリプトが実行されてしまいます。
クロスサイトスクリプティング(XSS) | トレンドマイクロ

最近はAngular/Vue.js/React等のframeworkを利用することが多いため、自力でDOMをレンダリングすることがほぼなくなりました。そのため、使い方に気をつければ、それほど気にしなくてもXSS対策はある程度できているはずです。

ただし、どれだけXSS対策を完璧にしていたとしても、HTML5のLocal Storageを使ってはいけない(翻訳)中に記載されている、第三者のJavaScriptコードが悪意のある第三者乗っ取られることが仮にあるとしたなら、自分達だけでの完璧な対策は無理かと思います。

まとめ

どちらも結局のところ、しっかりとした脆弱性対策をする必要があり、それ単体のみではセッションIDは安全ではありません。
ただ、今の所、JavaScriptからの攻撃を防げないという点でCookieを選ばざるおえないという判断なのでしょうか。
CORS時の保存先にCookieを選択する場合、サードパーティCookieとして認識されてブラウザにブロックされる可能性があることも頭の中に入れておく必要がありそうです。

この記事の中で間違いやこんな内容をもう少し書いてもらいたいなどありましたらお気軽にコメントお願いします!

94
68
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
94
68