JWT・Cookieそれぞれの認証方式のメリデメ比較

# JWTとは

* JSON Web Tokenの略。ジョットと読むらしい。
* ざっくりいうとJsonに電子署名を加え、URL-Safeな文字列にしたTokenのこと
    * 正確にいうと、電子署名を使用する方式はJWS（JSON Web Signature）と呼ばれ、別途暗号化を使用するJWE（JSON Web Encryption）も存在する。よく見かける説明はJWS方式のほう。
    * JWS方式の場合、電子署名であり暗号化ではないため、中身を見ることは可能。但し改ざんはできない、という仕組み。
* 実際のユースケースで言うと、サーバ側で認証情報が入ったJsonを加工（電子署名を加える等）し、JWTにしたのち、それを認証Tokenとしてクライアントに渡す。クライアントはそのTokenを認証Tokenとして使用する。

仕組みについては調べればたくさん出てくるのでそちらを参照したほうが良いかと思います。
この記事では、Cookieとの比較の話しか書かないです。

# この記事を書いた背景

サービス間通信の認証方式としてJWTの使用を検討していたのだが、
JWTの仕組みの説明は調べると結構出て来るが、
既存の仕組みとの違いの話があまり出てこなかった。

（サービス間通信は関係なくなるが）Cookieによる認証方式とTokenによる認証方式の比較をする記事があり、内容がよくまとまっていたため、自学のために内容の要約を書きたくなった。
https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
http://christina04.hatenablog.com/entry/2016/06/07/123000

# 内容要約

* 概要
    * CookieとJWTはいずれもHTTPリクエストのヘッダにそれらを載せて送信するという点では同じ。
![alt](https://cdn.auth0.com/blog/cookies-vs-tokens/cookie-token-auth.png)
※ 画像は[https://auth0.com/blog/cookies-vs-tokens-definitive-guide/](こちらの記事)のほうから引用。
    * 最も大きな違いはCookieがstateful・TokenがStatelessという点。
        * Cookieはサーバ側でsession情報を持つ。CookieにはsessionIdを入れており、リクエストの都度sessionIdに紐づくサーバ側sessionデータを参照している。
        * Tokenはクライアント側で「認証に成功した」という情報（＝Token）を持ち、リクエストの都度それを送る。
            * つまり意味論的にはsession（”一連の処理”）ではなく、各個処理は独立した処理として扱える

* Tokenのメリット
    * ステートレスでスケーラブル
        * 上述の通りステートレス。
    * パフォーマンス
        * Tokenの場合は署名の検証処理だけですむ
        * 一方CookieにsessionIdを入れていた場合は、sessionIdを元に、session情報を取得する（RDBやNoSQL DBから）処理が必要で遅くなる
    * Cross DomainとCORS
        * Cookieは異なるドメインに対して送信する際には色々制約がつく（CORS）
        * 一方JWTは（Cookieヘッダを使わない場合は）そういった制約はない。（後述するが、Cookieヘッダを使う方式もある）
    * データがJWT内に保存される
        * CookieだとsessionIdだけだが、JWTだとJSONなので、デコードしてやれば読むことができる
            * つまり（デコードしてやれば）フロントエンドから直接データにアクセス可能
            * デバッグが可能。[デバッグができるサイト](https://jwt.io/)
    * モバイル対応
        * Cookieを使用しないプラットフォームだった場合もJWTは使える
        * IoTとかの文脈でも出てくる認証方式みたいです

* Tokenについて気をつけるべき点
    * Tokenのデータサイズ
        * sessionCookieは小さい（sessionIdが入ってるだけ）。一方JWTはそれよりはでかくなる。
    * Tokenの保存場所
        1. Local storage
            * 大体の場合はこのパターンらしい。
            * local storageは特定ドメインに紐付いて保存するため、その他のドメインからはもちろんサブドメインからもアクセスできないので注意が必要。
            * XSSでLocal StorageにアクセスされTokenが盗まれることで、Sessionハイジャックが可能になるので注意。
                * （簡単に盗めそうだけど大丈夫なんだろうか？？受動型の攻撃だし、XSSの対策はHTTPヘッダやフレームワークレベルで色々防げるから大丈夫ということか？？）
            * 一方でCSRFの問題はない
        2. Cookie
            * 4KBのデータサイズ制約があるので注意。
            * secure属性・httpOnly属性をつければ、XSS脆弱性があってもセッションハイジャックは防げる
            * CookieヘッダでサーバへJWTを送る場合はCSRF脆弱性は残るので注意。
            * Cookie自体は単なる保存先として使用しAuthorizationヘッダでサーバに送る場合はCSRFを防げるが、上記のsecure属性が使えない（＝httpsでない場合に通信が見えてしまい、Tokenが盗まれる可能性がある）
        3. Session storage
            * Local storageの注意事項 ＋ ブラウザが閉じられたらTokenが消えることに注意。
    * Tokenの無効化（※記事内には記載はなかった）
        * 認証Tokenが盗まれた場合などに無効化できるようにする機構があったほうがよい
            * 認証Tokenの有効期限を短くするのは保険的対策になる
            * Blacklistを設定できるようにする
            * 本当に最悪のインシデントになった場合は、署名のアルゴリズムや秘密鍵自体を変え、全てのTokenを無効化する（全ユーザ再ログインが必要となる）

# その他 感想等

* Tokenバンザイな記事を取り上げたので、良い点ばかり上がってしまいました。
* JWT調べていてどこかで見たことある方式だなぁと思ったら、ScalaのPlay frameworkのsessionで使われていました。
（日頃業務で使っているのに、全然気づいていなかった……）
https://www.playframework.com/documentation/2.6.x/SettingsSession