いまさらJWTについて調べた

SSOを導入したいという相談を受け、いまさらながらOpenID ConnectやJWTについて調べてみたのでメモ。

参考

こちらが非常にわかりやすい。

qiita.com

JWS

ヘッダー.ペイロード(本文).署名 と、それぞれURLセーフなBase64エンコードされたJSONがドットで結合されている。

この形式は、JWS(JSON Web Signature)としてRFC7515に規定。

IDトークンでは、署名が必須とされているため、通常こちらが使われる。

JWSヘッダー

Googleの認証結果には以下が含まれていた。

algがnoneの場合、署名を省略可能。仕様上は有効だが、運用時はアルゴリズムのnoneやHS(共通鍵暗号)は使わず、公開鍵暗号を使用したほうがいい模様。

Critical vulnerabilities in JSON Web Token libraries

JWSペイロード

クライアントに対し、認証サーバーから実際に渡されるデータ。

「Claim Set」と書いてあるのもどこかで見た。クレームと聞くと苦情かと思うが、ここでは請求の意味。

JWS署名

ヘッダー.ペイロード を入力としたデジタル署名。署名形式はヘッダーのalg。

JWE

暗号化した形式、JWE(JSON Web Encryption)としてRFC7516に規定。

IDトークンとして使用する場合、JWSの ヘッダー.ペイロード.署名 を平文として暗号化したものが、暗号文として使われる。

今回は使わないので割愛。

JWK

JWSの署名やJWEの暗号化に、公開鍵暗号などの非対称鍵暗号が使用される場合、クライアント側で署名の検証や復号化を行うため、署名や暗号化に使われた鍵(公開鍵暗号での秘密鍵)と対になる鍵(公開鍵暗号での公開鍵)が必要となる。

その鍵の情報をJSON形式で表すための仕様。JWK(JSON Web Key)としてRFC7517に規定。

また、公開鍵認証の場合、以下が設定される模様。

  • use: 公開鍵の使われ方, sig(JWS) OR enc(JWE)
  • e: RSA暗号の公開指数(Public Exponent), 公開指数は65,537なので AQAB 固定と思われる
  • n: RSA暗号のモジュラス(Modulus), 2つの素数p,qの積
    • e, nともに整数値をOS2IP形式でバイナリ変換し、それをURLセーフなBase64に変換している

Google認証時の公開鍵

Googleの公開鍵は https://www.googleapis.com/oauth2/v3/certs で公開されている。

keysとして、いくつかの鍵情報が配列となっていた。

JWSヘッダーのalgおよびkidで検索し、公開鍵を特定する。

{
  "keys": [
    {
      "kid": "a06824b79e3982394d5ce7ac75bf92cba30a2e25",
      "e": "AQAB",
      "kty": "RSA",
      "alg": "RS256",
      "n": "tMrCJilHFRNB7Op6bhBSTpEEFCT4CM7zIOPT-HhjBhJ2bYahinC8FblyxE9rw889cS4eIAed9_614cQHUzv1lAgd3f-c0bonuMo_gGFJIOp5M4HlBz7yqimDDwYcSznSwtUziKM1pSCQr9IE-M-oNHd6ocXRhwKijzCIXIPvD4lPIjU5vR9rNziIume0AxfL8kAOIl2Rjcae8UmX24ydlLG1VGTiHuTcOzBZkGe5cAGHf4p4807PCihaSExWRQTbVrIfCIgBMehe1B99yf7ApKsHXVFN3pMsBso53jpL4XslJkOFI7SR0-gAvn89ieY5rGJ479srPDUBsZlSNtEiaw",
      "use": "sig"
    }
  ]
}

JWT

IDトークンを発行する仕組み。認証/認可の文脈でいうと、認証した結果として返されるトークン。

JSON Web Tokenの略称で、RFC7519に規定。ちなみに、読み方は「jot」

JSON形式のクレームをJWSのペイロードに設定、または暗号化してJWEの暗号文に設定したもの、となる模様。

Nested JWT

JWSでは署名、JWEでは暗号化しかできないため、両方を行いたい場合、どちらかの形式にもう片方を設定することとなる。

仕様では署名してから暗号化が必須となっているため、JWSを暗号化してJWEの暗号文に設定する。

Web経由ではhttps前提のため、暗号化は使われないっぽい?

とはいえ、通信プロトコルに依存はしていないと思われるので、暗号化されていない通信経路で使用する場合は、JWEやNested JWTで暗号化するのだろう。

Plaintext JWT

ヘッダに "alg": "none" が指定されている場合、 Plaintext JWT と呼ばれる。

そのまま、署名ないしは暗号化されていないJWTを指す。

仕様上有効とはいえ、通常はalgがnoneならエラーにするなどの対応が必要。

JWx

これらの仕様を総称し、JWxと呼ばれる。

OpenID Connectでの使われ方

OpenID Connectは、OAuth 2.0について、User IDの取得方法や、Profile APIの仕様について標準化したもの。

このUser IDの取得方法がIDトークン。そのIDトークンとして、JWTを使用することも記載されている。

openid.net

振り返り

別途まとめてくれている人がいくらでもいるのに、わざわざこんなの書く意味はないかと思ったが、やってみるといい勉強になった。