開発だとログインは最初に作るものなので、なかなか実装する機会もなく、ブラックボックス状態でしたが、基本の概念を整理してみました。
ログインとは
まず、ログインとはインターネット上のサービスを利用する際に、IDとパスワードを使って本人を確認する仕組みのことです。
そしてログインには認証と認可という2つのステップがあります。
・認証=本人であることをIDとPWで確認
・認可=鍵を渡して入室を許可
なぜログインが必要か?
もしAmazonにログインがなければ、毎回注文のたびに住所、氏名、支払い方法などを入力しないといけなくなりますし、もしTwitterにログインがなければ、他人になりすましての発言が容易にできてしまう。
なので、個人に紐付くデータを扱うサービスにはログインの仕組みが必要なのです。
HTTP通信はステートレス
HTTP/HTTPS通信は、毎回独立しています。
静的サイトしかない時代はよかったのですが、昨今のログインが必要なサービスなどでは、クライアントからサーバーへ毎回ID、PWを送らないといけなくなります。
しかし、それは大変なので、この問題を解決する為にCookieが生まれました。
*Cookieの正式名称はHTTP Cookie
ざっくり言うとサーバーは認証ができたクライアントに対して鍵を渡し、クライアントは以後サーバーとの通信時に、毎回その鍵を渡せば相互で識別できるようになり、ステートフルになる。
その鍵、身分証の様な役目をしているのがCookieです。
Cookieをさらに詳しく
Cookieとは、WEBサイトを閲覧したときに入力したデータなどの情報が記録されたテキストファイルのこと。利用するサイトのWebサーバーが発行し、ブラウザのStorageのCookiesに保管される。
例えばECサイトで商品を買い物カゴに入れてからWebサイトを閉じても、再訪問時に買い物カゴに商品が入っているという経験が誰でもあると思います。また、SNSなどのサイトにログインして、一定期間ログイン状態が保持される。
これらは皆Cookieのおかげなのです。
ブラウザの中に情報を保存するという点で、ローカルストレージとの違いは?
Cookieとローカルストレージでは結果としてできることはほぼ同じです。しかしローカルストレージとCookieでは情報の保存期間とデータ容量に差があります。ローカルストレージであれば半永久的に保存できますが、Cookieの場合は保存期間が制限されます。
またデータ容量においてもローカルストレージが5 MBなのに対し、クッキーは4 KBしかありません。またCookieの場合、通信を行うたびにデータ送信を行っているため、Cookieの情報が多ければ多いほど、重くなるという問題があります。
余談ですが、Cookieという言葉が指す範囲は広い。
サーバーから付与されるファイルの意味で使われたり、ブラウザのStorageにCookiesという場所があるので保管庫みたいな表現だったり、仕組みのことだったりと広いのですが、臨機応変に解釈しましょう。
2つの主要な認証方式
HTTP通信の認証は
・セッション/Cookieベース
・トークンベース
の2つが主に使われています。
セッション/Cookieベース:サーバーで認証状態を保持
セッションは本来Cookieをもとに成り立っている仕組みなので、両方の呼び名が混在しています。
ログインリクエストが来るとサーバーは、
・Cookie(ログイン情報などを含んだセッションID)を生成
・セッション変数を作成しDBに保存
・CookieをHTTPレスポンスのヘッダを利用して送る
・フロントはブラウザのCookieに保管し、以後、Cookie(セッションID)をリクエストヘッダーにつけてリクエストし、サーバー側では自身で持っているセッションIDと合致するかをみて、認証します。
・リクエストを受けたサーバーはセッションIDによって、DBに保管したセッション変数を取得して、フロントに返す。
*サーバー側はセッションIDが誰に対して発行したものかをわかっています。
*セッション変数を保存するのは、NoSQL(key, value型)がRDBより速いから便利なので主流
そして、Cookieにはセキュリティのための様々な属性がある
例)決まったドメインにしか送れないDomain属性や、JSで触れなくするHTTP Only属性など
トークンベース:フロントで認証状態を保持
ID、PWを渡して、ログインするとサーバーからのレスポンスにtokenが含まれる。
ブラウザのCookie/Local Storageに保管し、フロントは以後、トークンをリクエストヘッダーにつけてリクエストし、サーバーがそれを検証して、認証する。
アプリ内での滞在許可、ビザのようなもので、有効期間があり、切れると再ログインがいる。
銀行とかはこのtokenの有効期間は短く、厳しい。
tokenのフロント側の保管場所:
Cookie: 一般的だが、CSRFの危険性がある
LocalStorage: JSでアクセスして、どこかに送れてしまうので危険
実際の保管例:
A(AWS Amplifyを使っている)
ログインのタイミングでJWTをCookieなどに保管してない。
APIリクエストの共通モジュール内でtokenをAmplifyの機能でfetchして、ヘッダーにtokenをつけてる。
B
ログインした時にJWTをNookiesというライブラリでCookieに保管
APIリクエスト時にCookieに保管したトークンを取り出してリクエストヘッダーにつける
C
ログインした時にJWTをVuexのStoreに保管
APIリクエスト時にstoreに保管したトークンを取り出してリクエストヘッダーにつける
JWT
JSON Web Tokenの略で、ジョットと読む。
暗号化されたJSONで記載されたフォーマットのトークンによる認証方式のひとつ。
最近はこのJWTによるトークン認証が主流になってきている
セッション/Cookieベースとトークンベース、両者の違い
セッション/Cookieベース認証がサーバーに認証のための情報 (セッション ID とユーザー情報の対応) を持たせておく
トークンベース認証はサーバーにそうした情報を持たせずトークンの検証のみによって認証を行う
つまりは、
セッション/Cookieベース認証はステートフル
トークンベース認証はステートレス
であるということ。
セッション/Cookieベース認証がステートフルな通信を実現できるというメリットを持つ一方で、トークンベース認証は (サーバーに認証のための情報を持つ必要がないため) ユーザーが増加した時のサーバー負荷がセッションベース認証よりも少なく、よりスケーラブルなシステムを実現できるというメリットを持ちます。
セッション、トークンの無効:
セッションならログアウトで無効になる
トークンは普通にログアウトしても無効にならないので、そのtokenを盗まれると悪用される危険がある
ログアウトしてJWTを無効にする実装が必要
まとめ
完全なログイン認証はまだなく、脆弱性はある、スタンダードもまだない状態ですが、個人的にJWTによるトークン認証がスタートアップでは人気なのかなと言う印象です。