# 04ex. Session, Cookie, WebStorage, JWT ________________________________________ SPAセキュリティ超入門 https://www.docswell.com/s/ockeghem/K2PPNK-phpconf2022 SPAセキュリティ入門 https://www.docswell.com/s/ockeghem/ZM6VNK-phpconf2021-spa-security#p1 HTML5のLocal Storageを使ってはいけない(翻訳) https://techracho.bpsinc.jp/hachi8833/2019_10_09/80851 おーい磯野ー,Local StorageにJWT保存しようぜ! https://scrapbox.io/musou1500/おーい磯野ー,Local_StorageにJWT保存しようぜ! どうしてリスクアセスメントせずに JWT をセッションに使っちゃうわけ? https://co3k.org/blog/why-do-you-use-jwt-for-session JWT認証、便利やん? https://auth0.hatenablog.com/entry/2018/09/21/004131 Webアプリケーションのセッション管理にJWT導入を検討する際の考え方 https://ritou.hatenablog.com/entry/2019/12/01/060000 Cookies vs. Tokens: The Definitive Guide https://dzone.com/articles/cookies-vs-tokens-the-definitive-guide ________________________________________ ## 1. 前提の話 ________________________________________ ### 1.1. 枯れた王道:セッションID 要約 - 認証成功時、セッションIDを発行してサーバ側とCookieで互いに持つ モノシリックで素朴なシステムの実装ならこれ 1. ログイン認証時、セッションIDは再発行する 2. ログアウト時、セッション情報をクリアして破棄する 3. クライアントは、セッションIDのみを持つ 4. サーバは、原則としてセッション情報にアカウントIDのみを持つ。アカウント情報や認可情報はリクエスト毎にDBから取得する 5. LB配下ノードの場合、セッションストアにredisなどを使用する 6. リクエスト毎にDBから認可情報を取得するのが厳しいなら、アカウント情報と認可情報を期限付きでセッション内にキャッシュする - 認可情報のリクエスト毎取得がボトルネックなら、再取得の期限基準を10分程度にしても十分効果がある ________________________________________ ### 1.2. ステートレスアプローチ:トークンベース 要約 - 認証成功時、改ざん検出可能なトークンをクライアントに渡し、通常はクライアント側はCookieに保存しない SPAではこちらのアプローチの可能性もある 1. ログイン認証時、改ざん検出可能なトークンを生成して返す 2. ログアウトは本質的に制御不能。クライアント的にはトークンを破棄することで見た目上はログアウトする 3. クライアントはトークンを持つ。トークン自体に認可情報が含まれる 4. サーバは、トークンが改ざんされておらず、有効期限が切れていないことを検証する ________________________________________ ### 1.3. ステートフルとステートレス ステートレスにする動機の例 1. 認証サーバ、認可サーバ、セッションストアのランニングコストが高すぎる 2. セッションストア内へのキャッシュ等で解決不能なほどアクセスが多い 3. マイクロサービスが増え、リソースサーバ単体で簡単に認可を実現したい ※ いずれも、トークン等の情報が漏洩して有効期限が切れるまで使用された場合の被害を許容できるか?というトレードオフの要件ジャッジが必要になる 項目 |ステートフル |ステートレス ----------------------------------|----------------|------------------------------------ サーバサイドのセッションステート |有り |無し セッションステートを管理するサーバ|負荷が高い |そもそもない 代表例 |セッションID |Rails CookieStore、トークンベース 認可情報 |原則最新を再取得|署名つき平文または暗号化されたデータ内にある スケーラビリティ |低い |高い サーバサイドからの認証の無効化 |出来る |出来ない 有効期限とリスク |特に規定はない |要件によるが、長くとも1時間以内が推奨される CSRF対策 |必要 |Cookieに依らない場合は不要 それだけでの利用 |可能 |上記有効期限が無理なら非推奨 - ステートレスと有効期限とリフレッシュトークン 1. ステートレスは「サーバサイドからの認証の無効化」が不可能 2. ステートレスは漏洩時のリスクを軽減するために有効期限を短くすることが必須 3. それでも認証を自動で維持したい場合は、リフレッシュトークンの実装が必要 - 有効期限の長い「リフレッシュトークン」(ステートフル) - 有効期限の短い「アクセストークン」(ステートレス) セッションIDをJWTでラップする意味はあるか? - 調査中 ________________________________________ ## 2. JWT、JWS 要約 - 署名されたデータを標準仕様化したもの - URLに乗せる目的、署名検証目的、暗号化されたペイロード目的、といった多用途に対応できるようにしたため仕様が冗長 - ステートレスセッションを実現するために考案された仕様「ではない」 - そもそもステートレスセッション目的ならURLエンコードなんて実質無駄だし… - JWT:JSONをURLエンコードしたもの - JSONの各内容はJWT文脈ではclaimと呼ぶ - JSON自体はJWT文脈ではclaim setと呼ぶ - JWS:claim set(ペイロード)をJWSコンパクトシリアライゼーションしたもの - ヘッダ、ペイロード自体はURLエンコードされた平文 - 想定された本来の用途は、署名により発行者とペイロードを(改竄検証により)保証すること - JWSコンパクトシリアライゼーション - ヘッダ、ペイロード、署名をそれぞれURLエンコードして"."で繋ぐこと - JWSの署名 - JWSの内、ヘッダからペイロードまでの文字列を秘密鍵で署名(=復号操作)したもの - alg:none、alg:HS256、といった、標準仕様の欠陥がある。その穴埋めはJWTライブラリ任せである ステートレスセッションへの転用 - なぜか、現在ではステートレスセッションを目論んで転用される ________________________________________ ## 3. (認可キャッシュとしての)ステートレスセッション目的でのJWTの特性 前提 - あくまで認可キャッシュだけを考える。SPAのページ状態やform状態などは全く別の話 - 認証サーバとAPIサーバが別の場合についても、別途考えることがあるのでここでは考慮外 - 言い換えると、「モノシリックで素朴なシステム」なのに敢えてJWTを使ったらどうなるか、ということ 要約 - Railsに存在するCookieStoreに似た特性になる - 同じところ - トークンをサーバサイドだけで無効化できない - XSS対策に穴があれば、セッションIDと同じように無力 - 違うところ - Rails-CookieStoreは暗号化によって改竄を防ぐ。JWTは署名検証によって改竄を防ぐ - Rails-CookieStoreは暗号化しているのでユーザは内容が分からない。JWTはそのまま使用すると平文 - 内部犯行が発生した時、痕跡がほぼ残らない - そのため秘密鍵を定期的に変更することが推奨されるが、並行使用期間がないと一斉ログアウトする - サイズが大きくなりがちで、CookieではなくWebStorageとして保存することになりがち - httponlyやsecureと言ったXSSに対する予備策で守られない 要約の逆説 - 以下の全てを要件や想定を満たせるなら、他に何もせずに許容できる場合がある、かもしれない - XSS対策に穴があるわけがない。あるいは穴があっても被害が許容できる - 有効期間が短くて良い - 自動再取得であるリフレッシュトークンを実装しなくてよい 罠 - alg:none改竄を回避しなければならない - 検証側の公開鍵を公開してはならない(HS256改竄が通る) - 上述2つを回避する別の方法としては、標準への準拠をやめてalgを無視して固定した暗号方式を使用する - ペイロードは平文。これを回避するにはJWTの仕様外でトークン全体またはペイロードを暗号化&復号する必要がある - 有効期限がなければ、恒久的に有効なトークンになる