皆さん、こんにちは。
最近、ログイン機能を実装する際に、JWT(JSON Web Token)について調べたのですが。
今回はその調べた内容について書きたいと思います。
JWT(JSON Web Token)とは?
安全にJSONオブジェクトをやり取りするための表現形式。
HTTP認証ヘッダーやURIクエリーパラメータに、Tokenとして設置できる。
Header/Payload/Signatureの3つの要素から構成される。
3つの要素をdot(.)でつないだ形式が最終的なTokenの形式となる。
また、☝の3要素はそれぞれURL-safeな文字列(base64)で表現される。
Signature(サイン)は、Header.Paylaod
をハッシュ化したもので、受信時にデータ(Header&Payload)が改ざんされていないのかを証明することができる(完全性)。
具体的な使用例としては、
- ログイン認証後に発行するTokenをHTTP認証ヘッダーへ追加する
- ユーザ新規登録時に発行するEメールへ、URIのクエリーパラメータとしてTokenを追加する
など。
JWTとCSRFについて考えてみる
CSRF(Cross-Site Request Forgery)対策になるのかどうかについて考えてみたいと思います。
CSRF(Cross-Site Request Forgery)とは?
CSRFとは、外部のサイトから不正なHTTPリクエストを受け取り、処理してしまうことです。
簡単な図を用意しました。
例えば、攻撃者はある会社のWeb Serverに対して、なんらかの攻撃をしようとしているとします。
攻撃の流れとしては、
①攻撃者は、不正なHTTPリクエストを送信するためのサイトを用意する。
②攻撃者は、ある特定のユーザ or 不特定多数のユーザを攻撃用サイトに誘導する。誘導方法としては、メール/TwitterなどのSNSなどでメッセージを送信するなどが考えられる。
③ユーザAは、攻撃者が用意したサイトにアクセスしてしまい、自動でHTTPリクエスト(JSでformをsubmitなど)が送信される。
④ターゲットである自社Web Serverは、③のHTTPリクエストを受け付けてしまい、処理されてしまう。
補足:不正なHTTPリクエストというのは、意図しない書き込み・DDoS攻撃など、さまざま考えられます。
JWTでCSRFの不正リクエストは防げるのか
さて、ここからが本題です。
上の例をもとに考えてみましょう。
JWTの認証では、通常サーバサイドでToken中に含まれるユーザIDを参照して、このリクエストを送信したユーザは誰であるのかを確認しています。
そのため、Tokenが盗まれることさえなければ、問題ないです。
CSRF攻撃で送信されたHTTPリクエストはサーバサイドの認証ではじかれるので。
上の図では、ユーザAはすでにJWTの認証を行っており、Tokenを持っているとします。
ユーザAが攻撃者が用意したサイトを訪れたとしても、Tokenを取得できなければ、サーバサイドのTokenの認証ではじかれることになります。
上記のことから、JWTではTokenをどのようにフロント(ブラウザ)側で扱うのかが、CSRF対策として大切なことになってきます。
調べたところ、Tokenの管理には以下の3パターンがあるようです。
- Cookieで管理
- Web storageで管理
- メモリで管理
TokenをCookieで管理する
他のブログでもこれをお勧めしてましたね。
Cookie内にTokenを保存するにあたって、以下のことを実装する必要があります。
- JSからCookie内のTokenを読み書きできないようにHttpOnly属性を使用すること。
- httpsでのみCookieをサーバーへ送信できるようにするためにSecure属性を使用すること。
- SameSite属性をStrictまたはLaxに設定すること。
- CookieまたはTokenの持続時間(有効時間)を短くする。
TokenをWeb storageで管理する
Web storage(Local Storage, Session Storage)でTokenを管理する場合は、3rdパーティ製のJSからTokenを読み取られる可能性があるので、あまりおすすめしないとのことです。
しかしながら、Local StorageでTokenを保持しておくのが多くみられるパターンですよね。。。
Tokenをメモリで管理する
一番おすすめされていた方法が、このメモリでTokenを管理する方法です。
しかし、この方法は基本的にSPA(Single page application)くらいしか、使い道がないと思います。
JSの変数でメモリ上にTokenを保持した場合、ブラウザを閉じる or 画面をリロードすると保持していた値も消えるので、ログイン状態を維持することはできません。
まとめ
SPAの開発 && ブラウザを閉じた後もログイン状態を保持する必要がないなら、メモリ上にTokenを保持する。
それ以外の場合、基本的にCookie内でTokenを保持して使用する。
※ただし、上に記載した項目をCookie内に追加するようにする。
以上です!
次回は、Pythonで実際にCookieを使ってどのようにTokenを保持して認証できるのかを試してみたいと思います。