JSON ウェブトークンとは何か?
JSONウェブトークン(JWT)とはオープンスタンダード(RFC7519)であり、JSONオブジェクトとしてパーティの間で情報を安全に伝達するためにコンパクトで、信頼できる手段である。JWTは(HMACアルゴリズムを使い)秘密鍵またはRSAを使用したパプリックキーとプライベートキーのペアを使って署名できる。
JWTストラクチャー
JWTは基本的に「
.
」で分離された以下の3つの部分で構成されている:
- ヘッダー (header)
- ペイロード (payload)
- シグネチャー (signature)
JWTの形は以下の通り:
xxxxx.yyyyy.zzzzz
Isaax JWT
{
"get": [
// often used endpoints
],
"post": [
// often used endpoints
],
"put": [
// often used endpoints
],
"delete": [
// often used endpoints
],
"p": "96a30f99-5336-4a48-a412-e046409241fa",
"iat": 1489387792,
"exp": 1489391392
}
IsaaxアプリケーションのJWTの中に含まれている情報:
alg
– 暗号化アルゴリズム
typ
– 分類 (JWT)
- CRUDのアクセスルール
p
– ユーザーID
iat
– 発行日
exp
– 有効期限
ドットで分離されたJWTストリング(Base64)の方が、SAML等のXMLをベースとしたものに比べて、HTML・HTTPのような環境での取り扱いが簡単。
ヘッダーは、2つの部分で構成されている。一つ目はトークンのタイプ(JWT)を示し、二つ目はHMAC、SHA256またはRSAといったハッシュアルゴリズムを示す部分にである。
ペイロード (PAYLOAD)
トークンにおけるもう一つの主要部分が
ペイロードであり、claim (クレーム) がこれに含まれている。Claimとは、エンティティ(基本的にユーザー)についてのステートメントと、追加のメタデータとなる。
これには以下に示す3種類のclaimがある:
- reserved,
- public,
- private
Reserved Claim
これらは定義済のclaimで、強制ではなく推奨する程度だが、有用で、相互運用可能なclaimを一組提供する。
以下にそのいくつかの具体例を示す:
iss
(発行者),
exp
(認証有効期間),
sub
(サブジェクト),
aud
(オーディエンス) 等
注: JWTはコンパクトにするため、reserved claim名の長さは3文字となる。
Public Claim
これらは、衝突を避けるため、IANA JSONウェブトークンレジストリーで定義されている。
Private Claim
これらは、プライベートクレームの使用に同意したパーティの間で情報を共有するために作成されたカスタムクレームとなる。
シグネチャー
シグネチャー部分を作成するには符号化されたヘッダー、符号化されたペイロード、秘密鍵、ヘッダーに指定されたアルゴリズム、その署名をとる必要がある。
例えば、HMAC SHA256アルゴリズムを使用したい場合は、以下の方法で署名は作成する:
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload);
HMACSHA256(encodedString, 'secret');
この署名はJWTの送信者が述べている内容を認証し、そのメッセージがこれにより変更されなかったことを保証するために使用される。
JWTのロジック
認証の際に、ユーザーがクレデンシャルを使ってログイン成功すると、サーバーでセッションを保存したり、クッキーを返したりでするのではなく、ローカルストレージでトークンを保存しなければならないJWTが返される。(ローカルストレージが典型的な例であるが、クッキーも使用可能である)。
ユーザーが認証の必要なルートにアクセスしたい場合は、Bearerスキーマを使って
Authorization Header でJWTを送る:
Authorization: Bearer <token>
ユーザーのログイン情報がサーバのメモリに保存されないため、ステートレス認証のメカニズムが実現される。認証必須なルートへアクセスする際、サーバー側は
Authorization ヘッダーに有効なJWTか存在するかを確認する。もし、存在していれば、アクセスは許可される。JWTは独立型で、必要な情報はすべてトークンの中に含まれているため、データベースとの往復といったオーバーヘッドを最小限にすることが可能になる。Cross-Origin Resource Sharing (CORS)はクッキーを使用していないため、JWTを採用した認証メカニズムでは、どちらのドメインでAPIを提供しているかといった問題は存在しない。
JWT使用のメリット
JSONウェブトークンの使用を推奨するいくつかの理由は以下の通りである。
- URLのパラメーターやヘッダーとして使える
- 水平のスケールが容易
- デバッグと管理が簡単
- トラフィック対する負担が少ない
- 本来のRESTサービスを作成可能
- ビルトイン式の有効期限機能がついている
- JWTが独立型であること
JWT vs セッション
JSONウェブトークンが登場する以前は、サーバーベースの認証が優勢だった。HTTPプロトコルはステートレスなため、ユーザー名とパスワードで認証すると、次のリクエストで、アプリケーションはアクセスしているユーザーが誰なのかを知りようがなく、再度、認証を求めていた。毎回の認証の必要性をなくすため、ユーザーがログインしたあと、そのユーザーの認証ステータスが以後の毎回のHTTPリクエストでも継続して確認できる状態を保証することが必要であった。
セッションの場合、ユーザーのクレデンシャルは
POST リクエストとしてサーバーに送られる。サーバーがそのユーザーを認証する。もし送られたクレデンシャルが有効であれば、サーバーはユーザーを確認するための
SESSION_ID
が含まれるクッキーを使って返答する。ユーザーのセッション情報はファイル、またはデータベースに保存される。
スケーラビリティ:
アプリケーションが成長し、ユーザーのベースが増えるにつれ、水平もしくは垂直にスケールアップする必要性がでてくる。
水平にスケールするシナリオでは、アプリケーションサーバーが分散されるため、各サーバーがアクセスできる別のセントラルセッション保存システムを構築する必要がある。もししなかった場合、セッション保存障害のため、アプリケーションを拡張できなくなる。この問題を解決する他の方法としては、スティッキーセッションを使う。アプリケーションがクラウド環境でスケーリングしやすいように、ディスクメモリーにもセッションを保存することが可能であるが、このようなワークアラウンドは、巨大なアプリケーションやマイクロサービスアーキテクチャにおいては本当の意味ではうまく機能しない。
分散型システムを設定し維持するには技術的に高い知識が必要であり、余分な費用が嵩むという問題が出てくる。JWTを使えば、このような問題を解決することが可能になる。トークンベースの認証はステートレスなので、セッションにユーザー情報を保存する必要が生じない。アプリケーションはトークンを使用して、異なるサーバーからリソースにアクセス出来たとしても、心配をすることなく、簡単に拡張が可能になる。また、セッションを保存するための専用のサーバも必要がないので費用削減の効果もある。なぜならセッションがないからである。
セキュリティー:
署名済みJWTはクライアント側での改変を防止することを目的とすることになるが、署名済みJWTは、トークンが運ぶclaimの安全性を確保するために符号化も可能である。
JWTを直接ウェブストレージ(local storage)に保存する、あるいは、クッキーに保存するという二つの方法がある。JavaScriptは同じドメイン上でウェブストレージにアクセスする。これは、JWTがXSS(クロスサイトスクリプティング)の攻撃を受けやすいということを意味している。悪意のあるJavaScriptはページ上に埋め込むこともでき、Web Storageのコンテンツを読み込み、それを危険にさらすのである。非常にセンシティブなデータはXSSの攻撃を受ける可能性があるため、ウェブストレージに保存すべきでないと多くの専門家が主張している。
例: ユーザーのクレジットカード番号など、非常にセンシティブ/信頼できるデータと一緒にJWTがコード化されていないことを保証することである。
少し前は、JWTはクッキー内に保存するのが一般的だった。実際は、JWTはその都度クッキーとして保存されるが、クッキーはCSRF (Cross-site Request Forgery) 攻撃を受けやすい/影響されやすい。CRSF攻撃を防ぐ方法の一つは、クッキーが自分のドメインだけにアクセス可能にすることである。開発者は、JWTの使用の有無にかかわらず、CSRF保護に必要な対策を実装し、これらの攻撃を確実に避けられるようにすることが重要である。
最近では、JWTとセッションンIDは容赦のないリプレイアタックにさらされている。これらのシステムにどのようなリプレイ対策技術を施すかはすべて開発者次第である。この問題解決の代替策として、IPアドレスに対してJWTを発行することやブラウザーのフィンガープリンティングを使用することが考えられる。
注: HTTPS/SSLを使用して、クライアントとサーバーが通信中、自分のクッキーとJWTはデフォルトで確実に符号化されるようにしておく。これにより中間者攻撃を避けることが容易になる。
REST API サービス:
多くのアプリケーションでは、REST APIからJSONデータを読み込み、実行するパターンが使われている。さらに、他の開発者やアプリケーションが使えるように、REST APIを公開することが多い。API経由でデータを提供するメリットはたくさんある。例えば、データが1個以上のアプリケーションで使用できることが考えられる。ユーザークレデンシャルのためにセッションやクッキーを使う従来の方法ではこのケースにうまく対応できない。理由は、セッションやクッキーはステートをアプリケーションに取り込むからである。
REST APIの特徴の一つは、ステートレスであること。つまり、リクエストが実行されたとき、あるパラメーター内の応答であれば副作用が生じない。ユーザーの認証状態がそのような副作用を持ち込むとすれば、この原理を破ることになる。APIをステートレスに保つことで副作用が生じなくなり、これにより、保守やデバッグがかなりしやすくなる。
もうひとつの問題は、1個のAPIが一つのサーバーから使用され、実際のアプリケーションがもう一つのサーバーから使用されるという極めてよくある問題である。
CORS (Cross-Origin Resource Sharing)を有効にすると、これが生じる。クッキーは、そのクッキーが作成されたドメインでのみ使用可能なため、このアプリケーション以外の他のドメイン上にあるAPIにはあまり役に立たない。この場合、JWTを使って認証すると、REST APIがステートレスであることを保証し、このAPIやアプリケーションがどこから使用されるかを心配する必要もなくなる。
パフォーマンスを厳密に分析することは必要である。クライアントからサーバーにリクエストを実行するとき、もしデータの多くがJWT内で符号化されると、毎回のHTTPリクエストで無駄なオーバーヘッドができてしまう。それに比べて、セッションはオーバーヘッドが少ない。これはSESSION IDが実際はとても小さいものだからである。ただし、セッションを使うと、リクエスト毎にサーバー側のルックアップという負担が生じる。
JWTはクライアント側でデータを保持することにより、レイテンシーがトレードオフになる可能性があるため、アプリケーションのデータモデルが非常に重要な要因となる。データベースへの無駄な呼び出し削減・処理時間の短縮は、レイテンシー問題の解決策になる。無駄なリクエストを避けるために、過度のclaimをJWTの中に保存しないように注意する必要がある。
ダウンストリームサービス:
最近のウェブアプリケーションで見られるもう一つの共通のパターンは, ダウンストリームサービスに頼る傾向があるということである。例えば、中心となるアプリケーションサーバーへの呼び出しは、元々のリクエストが解決する前にダウンストリームサーバにリクエストをすることもある。ここでの問題は、クッキーは容易にダウンストリームサーバに流れないため、ユーザーの認証状態についてこれらのサーバーに伝えることができないということである。各サーバーは各々のクッキーのスキーマが異なる場合があるため、思うように流れず、サーバーまで到着するのが難しくなっている。ここでもJSONウェブトークンは難なくやってのられる。
まとめ
JSONウェブトークン(JWT)は取り扱いがシンプルで、プラットフォームや言語を超えて簡単に使用できる。JWTは、セッションなしで認証・権限を与えるためのスマートな方法である。JWTを署名したり、バリデーションするためのライブラリー豊富である。
当然ながら万能な対策はない。常にアプリケーションのアーキテクチャーとユースケースしだいなのである。]]>