🔥

セッション認証とトークン認証について

2023/02/15に公開
3

概要

セッション認証とトークン認証について整理する。下記サイトで詳しく解説されていたので、自分はあくまで学びをアウトプットする目的として本記事を作成する。詳しく学びたい人は、こちらで確認してほしい。
https://zenn.dev/tanaka_takeru/articles/3fe82159a045f7

早速だがセッション認証とトークン認証について、一言で言うと、セッション認証はサーバー側主体の認証方法であり、トークン認証はブラウザ側主体の認証方法である。

セッション認証

セッション認証は、ユーザがログイン成功した際に、ユーザ情報と紐付けたセッションIDを返す。ユーザはこのセッションIDをブラウザ上でクッキーとして保存して、サーバー側へリクエストを送る。具体的な流れを下記に示す。

  • ユーザがログイン認証を経て、サーバがユーザ情報と紐づけたセッションIDを発行する。
  • 発行されたセッションIDをユーザのブラウザが受け取り、クッキーとして保存する。
  • クッキーとして保存されたセッションIDを用いて、ユーザはサーバーに対してリクエストを送る。
  • サーバはリクエストされたセッションIDを確認して、対応するユーザの情報を取得する。
  • 取得した情報をもとに、そのユーザーがアクセス可能or不可能なリソースに対するリクエストをおこなっているのか判別して、その結果を返す。

具体例で、ユーザがAmazonでショッピングする場合を想定する。

  • ユーザはAmazonでログインする。そうすると、Amazonのサーバでユーザ情報と紐づける形でセッションIDを発行する。このユーザ情報には、ユーザがどういったアクションができるか書かれている。(*一般ユーザだから、商品の購入はできるけど、値段編集はできない等)
  • 発行されたセッションIDをユーザは、クッキーとして保存する。ユーザが商品購入、決済等のリクエストを行う際、このセッションIDを毎回サーバに対して送る。
  • ユーザが商品購入でこのセッションIDを送ったら、サーバ側でセッションIDとユーザ情報の照合をする。具体的には、「このユーザは一般ユーザだから商品購入はできるけど、商品編集はできないよね」といった情報を確認する。
  • このユーザに対して、認められたアクションのリクエストが来ていれば、200のレスポンスを返す。認められていないリクエストであれば、forbidden request等のレスポンスを返す

トークン認証

トークン認証は、ユーザがログインした際にアクセストークンを発行する(*アクセストークンは、セッションIDとは異なり、ユーザ情報と紐付けされない)。このアクセストークンには、そのトークンでアクセス可能なリソースの範囲(*下記の通り、権限範囲が記載されている)といった情報が格納されている。アクセストークンは、改ざんが難しいJWT形式が採用されることが多い。

具体的な流れを以下に記載する。

  • トークン認証は、ユーザがログインした際にアクセストークンを発行する
  • ユーザはアクセストークンを受け取ったら、ブラウザにクッキーとして保存する。サーバに対してリクエストを送る際、このアクセストークンを毎回一緒に送る。
  • リクエストを受けたら、まずサーバはアクセストークンの検証をして、アクセストークンが改ざんされておらず、有効期限にも問題ないか確認する。
  • サーバ側では、ユーザ情報とアクセストークンを紐付けて管理していない。そのため、サーバ側では「**というトークンであれば、**というリソースにアクセスできる」ということを事前に決めている。
  • 上記情報に基づいて、リクエストに対するレスポンスをユーザに返す

セッション認証とトークン認証の違い

セッション認証はステートフルな通信であり、トークン認証はステートレスな通信である。

セッション認証は、サーバにセッションIDとユーザ情報を持たせておく必要がある。サーバがユーザ情報を保持するため、ユーザ数の増加したときにサーバ負荷が大きくなる。なぜなら、ユーザ数が増加するとサーバがスケールアウトすることになり、複数のサーバでセッション情報を共有するためである。以前までは、スティッキーセッションと言って、セッションが続いている間は、同じサーバがそのユーザに対してレスポンスをかけるという、セッションの共有負荷がない方式も使われていたらしい。ただし、サーバがクラッシュしたときにセッション情報が消失してしまうというデメリットがあったそう。

一方で、トークン認証はそうした情報を持たせずトークンによる認証を行うのみで、サーバにセッションIDやユーザ情報といった認証のための情報を持つ必要がない。そのため、ユーザが増加した時のサーバ負荷が少なく、スケーラブルなシステムを実現しやすい。

参考:ステートフル/レスな通信とは

ステートフルな通信とは、状況によってレスポンスが変わること。つまり、以前までのやり取りを記憶した上で、臨機応変にレスポンスを返す「気が利く」イメージ。ステートレスな通信とは、状況によらず、あるリクエストに対するレスポンスが必ず同じ結果になるもの。つまり、以前までのやり取りを考慮せずに毎回同じレスポンスを返すので「気が利かない」イメージ。

ステートフルな通信の典型例としては、オンラインショッピングのカートが挙げられる。気に入った商品をカートに一時保存といったように、ユーザの過去の操作情報を保存して、後でその情報を活用であきる。

ステートレスな通信の典型例としては、「http」というプロトコルが挙げられる。これらから始まるURLへ接続した際、表示されるコンテンツは、誰が何度やっても同じWebサイトである。

引用

https://zenn.dev/tanaka_takeru/articles/3fe82159a045f7
https://milestone-of-se.nesuke.com/nw-basic/as-nw-engineer/stateful-and-stateless/
https://www.cloud-for-all.com/azure/blog/difference-stateful-and-stateless

Discussion

しーやんしーやん

セッション認証はサーバに認証するためのセッションIDとユーザ情報を持たせておく必要がある。そのため、ユーザが増加した時のサーバ負荷が大きく、スケーラブルなシステムを実現することが難しい。

スケールアウトしたときに、複数のサーバでセッション情報を共有する負荷が大きいということが分かるように書かないと、読者が分かったようで分かってない状態になる気がします。スティッキーセッションで対応するサーバがクラッシュしたときにセッション情報が飛ぶ代わりに、セッションの共有負荷がない方式も昔はよく使われました。

オライリーヨッシーオライリーヨッシー

しーやんさん、コメントありがとうございます!理解が曖昧な部分が多々あるので、ご指摘いただけて大変嬉しいです。以下の内容に書き直しました。

セッション認証は、サーバにセッションIDとユーザ情報を持たせておく必要がある。サーバがユーザ情報を保持するため、ユーザ数の増加したときにサーバ負荷が大きくなる。なぜなら、ユーザ数が増加するとサーバがスケールアウトすることになり、複数のサーバでセッション情報を共有するためである。以前までは、スティッキーセッションと言って、セッションが続いている間は、同じサーバがそのユーザに対してレスポンスをかけるという、セッションの共有負荷がない方式も使われていたらしい。ただし、サーバがクラッシュしたときにセッション情報が消失してしまうというデメリットがあったそう。

RyutaroMRyutaroM

このアクセストークンには、そのトークンでアクセス可能なリソースの範囲(*下記の通り、権限範囲が記載されている)といった情報が格納されている。

この説明的に、「トークン認証」というより「トークン認可」の方が正しいのでは?と思いました。
認証に使うべきはセッションで、JWTなどのトークンはOAuthで使われているように認可に使うものだと思います。(↓参考)

ユーザはアクセストークンを受け取ったら、ブラウザにクッキーとして保存する。サーバに対してリクエストを送る際、このアクセストークンを毎回一緒に送る。

また、ユーザがcookiesの中にトークンを保存するというのは適切ではない気がしました。
ユーザがcookiesの中にトークンを保存できる場合、cookiesのhttpOnlyをfalseにしているということだと思います。このとき、XSS攻撃を受ける可能性があるようです。(↓参考)

したがってcookiesの中にトークンを保存するのはユーザではなくサーバ側だと思いました。