全ての記事

cookie & session & tokenを理解する

2017-03-17 @sunderls

js





最近welogger.comを開発していて、使ってるサーバーがめっちゃ遅くて、service workerを利用してパフォーマンスを向上させようと思ってた。でもhtml上でのcrsf_tokenがcacheさせる問題があり、sessionが無効になる。解決するためにはtokenの形でやるのが良い気がして、laravelもpassportを提供してる。作るにはちょっと調べました。ここはメモです。

参考: https://auth0.com/blog/cookies-vs-tokens-definitive-guide/

1. cookie baseの認証

httpはstateless、サーバー上では来者がわからない。毎回パスワードをつけてもいいけど、セキュリティ上ではダメだ。それで案がある:

  1. ユーザーlogin
  2. サーバーで会話(session)が作られ、sessionの中にユーザーid、来た時間などが含まれる。sessionIdはブラウザーへ返される。
  3. 次回アクセスするとき、sessionIdをつければ、サーバー上でユーザーが特定できる。

cookieはストレージの一つ、domainごとでブラウザー上に存在する、domainAにアクセスすると自動的にdomainAのcookieが連れられる。cookieを利用してsessionIDを保存するのは明白に良いのだ。

logoutするとき、sessionを削除、cookieのsessionIDも。

2. token baseの認証

ok, tokenは「お前の名前を教えてくれ、信じてやる」

  1. ユーザーlogin
  2. user特定できるtokenが返される。とくにサーバー上保存することない
  3. 次回のアクセスにはtokenをつけるとよい。

このtokenはlocalstorageやcookieなどどっちでもよい。logoutするとき、ブラウザー側削除すればよい

3. 簡単に比べる

一見みると、大きな違いはなく、どちらも何かを送る・もらう・保存する。でも例えば役所に行って何らかの手続きとする:

  1. 身分証を渡して、確認できたら、番号ふたが渡される。以降どの場合もその番号蓋を渡す。
  2. 毎回身分証を渡す。

ちょっと違いがありますね(あまりいい例がないかも)。2番がお互いに簡単、だから身分証が大事にしないといけない。

4. tokenのいいところ

この記事に参考してます。

4.1 stateless & scalable

サーバーでセッションを保存しなくてよいので、スケールしやすい。複数サーバーがあると、会話の保存が複雑になるよね。sync方法を設けるか、またuserとserverのマッピングを知る方法など。

4.2 sso

ブラウザーでは基本的にcross domainがダメだ。cookie-sessionだとloginするとき、sso endpointなどを通じて、全てのdomainにcookieを焼かないといけない(iframeとか)。

tokenを使えば、違うsiteでも認証方法が同じであれば、通る。

4.3 tokeに情報入れられる

ちょっと後でjwt(json web token)を見ましょう

4.4 性能

tokenではdbを見ない、ユーザーの情報などtokenから取れる

4.5 ブラウザーじゃないでもサポート

例えばアプリとか

5. tokenの問題

5.1 tokenのsize

sessoinIdが小さい、tokenが割と大きい。でも大した問題じゃなさそう

5.2 tokenの保存

localStorageに入れると、cookieみたいに親のcookieが取得することがない。cookieに入れると、4kbの制限に縛られる。パッとみlocalStorageの問題が大きい気がする。

5.3 xssとxsrf

xssを簡単に言うと、外部スクリップトがこっそり入った。いろいろケースがある、例えばユーザーの入力がやばかったや、3rd party scriptがやばいとか、この場合は、tokenが敵に対してはだかだ。でも普通xssはframework レベルで解決されると思う。

xsrfは違うサイトでユーザーが騙される本体のsiteへリクエストほ走らせた。例えばAにアクセスして、知らないうちにBにpostしちゃった。browserではcookieがついてるので、削除や振込などが発生するのは可能だ。なので、postなど重要なアクションの場合は、このアクションが信じられるwebsiteで発火したかどうかの確認が必要、例えば信じるpageだけにランダムの文字列(csrf_token)を入れる、リクエスト受けたらそのtokenを確認する。

もしiframeでクリックさせるのでは?これはhttp header X-Frame-Optionsの設置で防ぐ。

もしtokenをlocalStorageにいれると、ブラウザーのセキュリティで第三者が知らない。なのでcsrfは問題ない。xssが可能、対応するにはhttp header cspが有力。

でもcookieにいれると、httpOnlyflagでjsの取得を禁止できる、xss問題は防げる。でもcsrf問題が起こる、しかもcookieがいろいろ別の問題がある、例えばhttp/httpsのcookieが共用されるとか。

ちょっと別のことを書いておく

  1. MITM: httpsで防ぐ。とくに考えなくて良い、なぜか考えてもとくにhttps以外には対策がない。
  2. 失効问题: tokenの再発行が必要

6. JWT

jwtは大人気、事実上のスタンダードになってるらしい

https://jwt.io/ からのものです。

  1. Header
  2. Payload
  3. Signature

この三つの情報で、token: xxxx.yyyy.zzzzを生成する。 header

{
  "alg": "HS256", // アルゴリズム
  "typ": "JWT"    // tokenタイプ
}

↑をBase64Url変換してxxxxを生成する。 Payloadの中にいっぱいclaimがある、例えば俺はだらだとか、俺はなにができるのか。(Reserved, Public, private)三種類に分類される。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

↑をBase64Url変換して => yyyy xxxxyyyyを連結して、headerにあるアルゴリズムでwebsiteが指定するsecretでhashする:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

これでzzzzが得る。

secretがサーバー自分が持っていて、第三者がどうしても有効なtokenを生成できる。つまり印鑑っていうものですね。

7. まとめ

勉強になりました。



如果觉得有帮助到你的话,
欢迎支付宝donate