Reactでの認証は厄介です。
Reactでコードを書いているとReduxやら外部ライブラリやら様々な「覚えること」「理解すべきこと」が出てきますが、結局ググればなんとかなることが多いです。
私がReactでアプリケーションを作るにあたって最も頭を悩ませたのが、今回取り上げる「認証」です。
認証はとにかくググっても結論が見いだせず、途方に暮れました。ネット上には様々な意見があったのですが、それらをまとめつつ私なりの結論を書いていきたいと思います。
こんな方におすすめ
- これからSPAの認証を学びたいという方
- 認証について調べてみたけど、「つまり、、、どういうことだってばよ、、?」となってしまった方
注意
このページでは私が調査し実践している内容をまとめ、紹介しています。しかしながら何かを保証できるわけではありませんし、実践する際は自己責任でお願いします。
Contents
Reactでの認証
ポイント
トークン(JWT)認証について知っているという方は読み飛ばしてください。
Reactを始めとしたSPA(Single Page Application)では一般的にトークンを用いた認証方式が用いられます。そもそも今までのWebアプリケーションフレームワークなどではセッションベースの認証方式が一般的でした。
今までの認証方式
簡単に言えばクライアントサイドにはサーバーから「セッションID」と呼ばれる文字列が渡され、サーバーサイドのデータベースにはその「セッションID」と「ユーザ情報」を結びつける情報を保存しておくことで「誰が認証されているか」という判別をつけていたわけです。そしてCookieと呼ばれる仕組みを用いてクライアント側では特別な処理をしなくても自動で「セッションID」をやりとりすることができたのです。

Cookieを介してSession IDをやり取りする
しかし、SPAではサーバーサイドにそのようなユーザ情報を保存しないというのが一般的です。
ではサーバーサイドに情報を保存しないのであれば、どこに認証情報を保存すればいいのでしょうか?
当然クライアントサイドということになります。サーバーサイドから発行されたトークンと呼ばれるものをクライアントサイドに保存し、それをサーバーへのアクセス時に送ることで認証を行います。
トークン認証(JWT)
このトークンとしてよく使われるのがJWTトークンと呼ばれるトークンです。

やり取りするのはトークン。トークンはクライアント側に保存される。
これはこれでググってほしいのですが(記事が長くなりすぎるので)、簡単に言えば認証情報をエンコードし、文字列(正確にはJSON)にしたものです。つまりトークンの中に情報が入っているということです。そしてこれは簡単にデコードすることが出来るので、盗まれたくない情報です。
ポイント
重要な点としてAccess TokenとRefresh Tokenがあるという点は述べておきます。Access Tokenはサーバーへアクセスするためのトークンですが厳しい有効期限があります。リフレッシュトークンはそれを更新するためのトークンです。
詳しくはこちらを参照してください。
しかしながら、この記事のタイトルから想像できるように、クライアントサイドに情報を保存するということはセキュリティ上の問題が生じるのです。
「どこに何を保存するのか?」という問題です。よく紹介されているのがトークンをブラウザのLocal Storageへ保存する方法です。
一方でこの方法を「絶対にやるべきではない!」という意見もあります。
結局どちらを信じればいいのでしょうか?
ポイント
長い記事になりますのでまずは結論から述べたいと思います
私なりの結論
結局の所、「どれだけ強固に守る必要があるのか」というのがポイントでしょう。
扱う情報の機密性が高くない場合
例えば個人や仲間内で使うようなアプリで、使う側がリスクを認識した上で使うのであれば、Local Storageにアクセストークンを保存しても問題はないでしょう。
ただしその場合でもアクセストークンの有効期限は短く設定すべきですし、トークンには重要な情報(パスワードなど)を含めてはいけません。
パスワードは他のサービスで利用していない独立したものであるべきですし、流出して困るような情報は扱わないようにしましょう。
注意ポイント
パスワードの変更もトークンだけで出来ないようにすべきです。必ず現在のパスワードをもう一度入力してもらうような仕組みを作りましょう。
また下記の「共通して守るべき項目」は満たしていなくてはなりません。
扱う情報の機密性が高い場合
この場合は"絶対に"いかなる機密情報もLocal Storageに保存してはいけません。自宅の玄関に通帳をおいておくようなものです。ダメ。
それならどうするか。
認証を自分でやらないというのが現実的な手であると思います。Auth0など外部の認証サービスを利用するのが確実です。Auth0に関しては多くの記事があるので調べてみるといいでしょう。(このブログでもゆくゆくは紹介する予定です。)
もしくはトークン認証を諦めるという選択も多いにアリだと思います。
共通して守るべき項目
認証に関しては様々な意見があるものの、誰もが共通して「これは絶対やっとけよ」というものもあります。
- 暗号化通信を使うこと。つまりHttpsで通信すること。Httpはダメ。
- XSS対策をすること(フレームワークの指針に従うべし)
Http通信に関してはReact(やその他SPA)に固有の話ではないので省略します。
基本的にはどのメジャーなフレームワークもXSS対策はされていますが、例えばReactではdangerouslySetInnerHTMLのようなことを何も考えずに使ってしまうと問題が起こります。
文字通りdangerouslyと言ってくれているわけですから使わなければいい話なのですが、どうしても使わなくてはならない場合はそれなりの対策をしなくてはいけません。
注意ポイント
もしReactでdangerouslySetInnerHTMLを使う場合は自分で正規表現を書いて対策をするのではなく、DOMPurifyのように外部ライブラリを使用しましょう。自分で書くとバグが発生しやすいです。
ここからはこの結論に至るまでの調査内容をまとめていきます。
「Local Storageを使ってはいけない」について
日本語の記事であればこちらがとても参考になります。
もうこの記事を読むだけでLocal Storageを使う気がなくなりますし、実際使うべきではないのでしょう。この記事の中でも書かれている通りこの意見は多数の知見のある方たちの意見であり、一部の神経過敏な方が騒いでいるわけではありません。
ここで書かれているのは結局のところLocal Storageではなく「サーバーサイドセッションを使え」ということです。そしてCookieを細かく設定しろということが述べられています。
ちなみに個人的に最も参考になるのはこの記事の原文のコメント欄です。この記事に同意する方や同意しない方も含め多くの意見が述べられています。時間があれば読んでみると大いに参考になるでしょう。
ポイント
原文のコメント欄で筆者は、「ほとんどの開発者がリスクを全く考えていない」「JWTやLocal Storageは認証情報を扱うために作られたものではない」ということを強調しています。
「Local Storageを使ってはいけない」に対する反論
一方でLocal Storageを使うな、という意見に対して反論もあります。
私が個人的にわかりやすいと思った意見としてこういった意見があります。海外掲示板上でのやりとりなので"意訳"です。
まずLocal Storageが危険にさらされるシチュエーションとしてはXSSの攻撃がある。JavaScriptのコードが自分のアプリ上で実行されてしまえばLocal Storageから簡単にトークンを盗み出すことが出来る。
ただし、そのような状況(悪意のあるコードが自分のアプリ上に埋め込まれた場合)ではもはやLocal StorageだけでなくCookieも含めた全ての情報が危険にさらされるということである。
これはXSS対策をしておくべきという問題であり、「Local Storageに保存すべきではない」という問題ではない。
そしてReactはデフォルトでXSS対策がされているのでそこまで過敏になる必要はない。
という意見です。
つまり「問題の本質はLocal Storageではないだろ?」という意見ですね。
ただし、CDN(広告なども含む)のような外部サービスを介して悪意ある攻撃をされた場合はLocal Storageは危険にさらされることになります。このリスクを考える必要があるため、企業のように高いセキュリティが必要(かつ広告などの外部サービスを利用せざるを得ない)場合は、やはりLocal Storageは向いていないといえます。
Local Storageを使わないならどうするのか?
上で紹介した記事にも出てくるのですが、Local StorageではなくCookieを使うというやり方があります。
どちらの方がいいのでしょうか?
これが人によって言っていることが違っていてかなり混乱しました。(実際のところ前提条件や判断基準の違いなどによるものと考えられます。)
気になる方は以下の記事を見てみるといいと思います。
Local StorageはXSSに脆弱であるためCookie使え派
要約するとLocal StorageもCookieも完璧ではないけどCookieを使うべきと言っています。(ただしその後の返信で反論されていますが)
より具体的な話を交えてこういった記事もあります。
こちらはより複雑で、JWTを分解して別々の設定のCookieに保存すべきということが述べられています。
Local Storageでいいよ派
どちらの記事も基本的には同じような事が言われています。Local Storageは確かにXSSに対して脆弱であるものの、CookieはCSRFに対して脆弱であり、どっちもどっちだと言うことです。
また、2つ目の記事ではCookieもLocal Storageと同様にXSSに対して脆弱であると述べています。
それ以外の選択肢
私が調べた限りですが、「セキュア」であると述べられている記事のやり方は大抵外部のライブラリ(認証システム)を利用しています。Auth0がメジャーです。
私個人の意見ですが、本当にセキュアにアプリを保ちたいのであれば認証周りを自分で実装すべきではないと思います。というかサーバーサイドセッションをSPAで使うだとか、Cookieを厳格に細かくセッティングするのは現実的にはかなり難しいと思います。
結論に戻る
基本的には上の「私なりの結論」に書いたとおりです。
重要な情報を扱うプロダクトでは、外部サービスの利用を含め万全に対応すべきでしょう。
とはいえ個人で「なんか作ってみたい」という人や、仲間内での使用ならそこまでビビる必要もないのではないかと考えています。リスクを認識・対策した上で使いましょうという話です。
まとめ
今回はReactでの認証に関する問題を取り上げました。一から調べていると膨大な時間がかかってしまうので、その足がかりにでもなれば幸いです。
冷静に考えてみると認証のような面倒な事柄を全てやってくれるからこそFull Stackのフレームワークが人気があったわけで、それを使わないのであれば厄介な問題に向き合うのは当然ですよね。
そういった覚悟もなしに使うべきではないのかな〜とちょっとションボリしました。
最後に一つだけ
最後に私がオススメしたいReactのオンデマンド講義について紹介しておきます。
Reactを扱った動画の中で最も良かったのがUdemyのReact - The Complete Guide (incl Hooks, React Router, Redux) です。英語の講義ですが、日本人でも聞き取りやすい英語で、何より内容が質・量ともにトップクラスです。
Udemyを使ったことのない方は割引価格で購入することが出来るはずです。またすでにUdemyを使ったことのある方はセール時に購入することをオススメします。(セールは頻繁に行われます)
長い記事でしたが最後まで読んでくださった方、ありがとうございました🙏