文字で読みたい2分間OAuth講座 : (7) When can one use password grant?, (8) ROPC (Password Grant) the Migration Tool

こんばんは、ritouです。

まだNatさんのチャンネルをサブスクライブしていないの?

www.youtube.com

前の記事 : 文字で読みたい2分間OAuth講座 : (5) Secret of Authorization Code, (6) Actors of OAuth - r-weblife

では、今回も見ていきましょう。

(7) When can one use password grant?

www.youtube.com

今回は、Resource Owner Password Credentials Grant Type (ROPC) の使い所について説明します。 一言で言うと「使ってはいけません」なのですが、それだと2分間にならないのでもう少し説明します。

ROPCは、ユーザー(Resource Owner) が自らのIdentifier, パスワードを Client に渡し、 Client はそれを Authorization Server に渡して Access Token を取得するフローです。

f:id:ritou:20180926025809p:plain

UIで言うと、これらはユーザー認証にあたりバックエンドのLDAPやDBサーバーに問い合わせる、(OAuthが登場する前からある)レガシーなアプリケーションと同様です。

f:id:ritou:20180926030511p:plain

仕組みとしてレガシーアプリケーションよりもROPCで改善されている点としては、Client が Resource Owner のパスワードを保存することなく、タスクを実行できることです。

f:id:ritou:20180926030812p:plain

しかし、ここでClient は"中間者"としていわゆる"中間者攻撃"を行なっていることに注意すべきです。

f:id:ritou:20180926031506p:plain

Client は Resource Owner のパスワードを取得しているので、Resource Owner ができることは Resource Owner に成り代わって全てできます。 (Authorization Server が Resource Owner に対して Client からの要求に対する許可を問い合わせる Authorization Code Grant とは異なり、) Resource Owner が実際に許可を与えたかどうかを Authorization Server はわかりません。

Authorization Server が Client に全幅の信頼を寄せていても、これはしても良い事ではありません。 なぜなら、Resource Owner をフィッシング攻撃に脆弱になるように訓練しているようなものだからです。 ある意味では、これはインターネットに公害を垂れ流し、他人にコストを押し付けていることになります。

悪いことは他にもいくつかありますが... Scott Brady さんの記事は、この件に関する良い概論となっています。

www.scottbrady91.com

せっかくなので目次だけでもざっと載せると、

  • ROPCがなぜ存在するのか
  • ROPCを使うべきではないたったn個の理由
  • ROPCを使う前の自分自身への4つの質問

が書いてあり、まとめとしては

  • (著者が)唯一 ROPC の利用を認めたのは、2000年前半のレガシーアプリケーションからの移行の時だけ
  • ブラウザベースのクライアントアプリケーションなら Implicit Grant使え
  • WebアプリケーションならAuthorization Code Grant使え
  • IoTデバイスなら Device Flow使え

とのことです。

If you use this grant type despite the above, I will be nasty to you and say mean things about you both behind your back and to your face.

"say mean things about" は意地悪なことを言うって意味ですな。 say mean things a...の意味・用例|英辞郎 on the WEB:アルク 何やら穏やかではない。

んで、最後にこの動画が貼り付けられています。循環参照的な...

話を戻します。 もしモバイルアプリケーションを書いているならば、IETF BCP212 が参考になります。

BCP 212 - OAuth 2.0 for Native Apps

まとめると、

  • ROPC は Resource Owner のパスワードを扱うフローであり、推奨されていない
  • RFC6749に記載されているのも、レガシーアプリケーションからの移行が理由である。もう7年前だぞ。
  • 要するに、使うな

そして、第8回はそれでも食い下がって来る人向けのお話のようです。

(8) ROPC (Password Grant) the Migration Tool

www.youtube.com

前回、ROPC使うなって言ったら、仕様に書いてるから使っていいだろって言われました。 ほんとかどうか見ていきましょう。

ROPC は RFC6749 に記載されていますが、こうも書かれています。

The authorization server and client SHOULD minimize use of this grant type and utilize other grant types whenever possible. Authorization Server と Client はこのフローの利用を最小限とするべきで、可能な限り他のフローを使うべき

なので、一言で言うと、「使うな」と言うことです。 では、なぜ記載されているのか。ROPC の使い所が一つだけあります。 それは、「パスワードを扱うレガシーな Client の OAuth 2.0移行」のためです。

今回は、その移行方法について説明します。

まず、パスワードを保存しているレガシーなアプリケーションがあります。

f:id:ritou:20180926112530p:plain

まず、ROPCのフローを実装してアプリケーションを更新します。

f:id:ritou:20180926112620p:plain

Client は保存してあるパスワードを Authorization Server に送り、Access Tokenを取得します。

f:id:ritou:20180926112729p:plain

保存してあるパスワードは削除。

f:id:ritou:20180926112816p:plain

代わりにAccess Tokenを保存します。

f:id:ritou:20180926112837p:plain

これで、裏側はAccess Tokenをベースでデータをやり取りする、OAuth 2.0 の Client に移行できました。 これだけであれば、ユーザーにパスワード入れなおしてもらわなくてもできます。"Zero Friction Migration!!!"

f:id:ritou:20180926113057p:plain

裏側はAccess Tokenベースになっているので、後はアプリケーションの特性に合わせて Access Token 取得までのフローを改修してやれば良いです。

f:id:ritou:20180926112057p:plain

まとめると、

  • ROPCがRFC6749に記載されている理由は、レガシーアプリケーションからの移行のためである
  • パスワードを保存しているレガシーアプリケーションは、ROPCで Access Token に変換して保存することで
  • 保存してあるパスワードでごにょごにょやるのでユーザーにパスワード聞き直す必要もない
  • このケースじゃない場合、別のフローを使うこと考えなさい

と言う感じですね。

これで9/26時点で最新の第8回に追いつきました。 これからできることは、チャンネルをサブスクリプションして動画を再生し、Natさんのモチベーションを上げることです。

www.youtube.com

ではまた。

文字で読みたい2分間OAuth講座 : (5) Secret of Authorization Code, (6) Actors of OAuth

こんばんは、おはようございます。 ritouです。

文字で読みたい2分間OAuth講座 : (3) Where are Sender Constrained Tokens used?, (4) Why Refresh Token? - r-weblife の続きで、今日は第5,6回の内容を紹介していきます。

動画をまだみてない人は、まずチャンネルチャンネルをサブスクライブして見てください。

www.youtube.com

では見ていきましょう。

(5) Secret of Authorization Code

www.youtube.com

これまでに、Access Token と Refresh Token を取り上げてきました。
今回はもう一つのトークン、 Authorization Code についての説明です。

Authorization Code は、ブラウザを経由して、Authorization Server から Client に渡されます。

f:id:ritou:20180925003339p:plain

仕様やフローの話をしているときに Authorization Code はトークンと呼ばれませんが、実際はトークンであり、Client の要求に対する Resource Owner(いわゆるユーザー) の許可を具体化しています。

f:id:ritou:20180925003852p:plain

そして、Authorization Code は一般的に、User Constrained Token です。(User Constrained Token とは?となったら振り出しに戻りましょ! 文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens - r-weblife)

  • Authoization Code は Client に紐づけられている
  • Client は Client ID / Secret と共に Authorization Code を Authoization Code を送り直すことで、自分が紐づけられた Client であることを証明しつつ Access Token などを要求する

よって、Authorization Code のみを第3者に取得されても"大きな問題"にはなりません。 (モバイルアプリケーションなどで Client Secret を安全に保管できない環境で Authorization Code を取得されると Client になりすましてAccess Token などを取得されてしまうリスクはありますし、その対策もありますがここでは省略します。)

なぜこのようなステップを踏んでいるのか、その理由は Authorization Code が Client に渡される経路にあります。 Authorization Code は Resource Owner のブラウザを経由して(リダイレクトを利用して) Client に渡されます。 そのため、通信が完全には守られておらず、この経路で発行されるトークンはサーバー間の通信でやり取りされるよりも第3者に取得される可能性があります。

f:id:ritou:20180925005703p:plain

よって、このような経路で Bearer Token を送るのは BAD Idea ということです。(Implicit Grant についてはまた別途...)

f:id:ritou:20180925005901p:plain

ということで、第5回の内容は

  • Authorization Code というトークンがある
  • ブラウザ経由で送られることで盗まれる可能性があるため、User Constrained である

というあたりでしょうか。

(6) Actors of OAuth

www.youtube.com

第6回は、OAuth 2.0 における登場人物について説明します。

RFC 6749 - The OAuth 2.0 Authorization Framework では4種類の登場人物が出てきます。

f:id:ritou:20180925023909p:plain

  1. Client : Protected Resource へのアクセスを行いたいエンティティ。地下鉄の例でいうと、乗客。
  2. Authorization Server : Client に各種トークンを発行するエンティティ。地下鉄の例でいうと、券売機。
  3. Resource Server : Client が Access Token を用いてアクセスする先。地下鉄の例でいうと、改札で守られた中のサービス。
  4. Resource Owner : Client にリソースアクセスを許可する人。地下鉄の例では直接出てこなかった。

Resource Owner について、地下鉄の例では、乗車料金を決める地下鉄会社の社長がその立場にあたります。 券売機では料金を払えば改札を通れる切符が買えます。そのため、実際は認可を求められるたびに出てきて許可を与える必要がありません。 SNSのユーザー情報へのアクセスを外部のサービスが要求するような場合、ユーザーが Resource Owner になって Client に許可を与えます。

ということで、第6回は OAuth の登場人物についてのお話でした。

次回予告

次回は、

  • (7) When can one use password grant?
  • (8) ROPC (Password Grant) the Migration Tool

という、パスワードを扱うフローについての回を説明します。 気になる方はチャンネルをサブスクライブして今から見ちゃいましょう。

www.youtube.com

ではまた。

FAPI : JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) とは

ritou です。

みんな大好きJWT。今日もみんなで eyJ!ということで、今回はこちらの仕様について説明します。

openid.net

概要

This document defines a new JWT-based mode to encode authorization responses. Clients are enabled to request the transmission of the authorization response parameters along with additional data in JWT format. This mechanism enhances the security of the standard authorization response since it adds support for signing and encryption, sender authentication, audience restriction as well as protection from replay, credential leakage, and mix-up attacks. It can be combined with any response type.

この仕様を3行で説明しろと言われたらこんな感じでしょう。

  • JWT形式で Authorization Response を返すための仕様です
  • Response Mode を指定するための response_mode パラメータも拡張する
  • Client / Authorization Server それぞれの metadata も拡張する

まずは Response Mode の振り返りからはじめましょう。

これまでの Response Mode とは

response_mode パラメータが定義されているのは、この仕様です。

openid.net

Response Mode

The Response Mode determines how the Authorization Server returns result parameters from the Authorization Endpoint. Non-default modes are specified using the response_mode request parameter. If response_mode is not present in a request, the default Response Mode mechanism specified by the Response Type is used.

Response Mode は Authorization Server が(認可処理の)結果のパラメータを Authorization Endpoint から「どのように返すか」を決定します。
デフォルト値ではないmodeは response_mode というリクエストパラメータを用いて指定され、指定されない場合は Response Type に対応した デフォルトの Response Mode が利用されます。

OAuth 2.0 の Response Type で表現すると

  • response_type=code : いわゆる Authorization Code Grant であり、Authorization Response はクエリパラメータとして指定される -> デフォルトの Response Mode は query
  • response_type=fragment : いわゆる Implicit Grant であり、Authorization Response はフラグメント部分に含まれる -> デフォルトの Response Mode は fragment

という感じになります。

また、こちらの仕様により response_mode パラメータに form_post が追加されました。

openid.net

例を見て見ましょう。まず Authorization Request に response_mode パラメータが指定されます。

GET /authorize?
   response_type=id_token
   &response_mode=form_post
   &client_id=some_client
   &scope=openid
   &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcallback
   &state=DcP7csa3hMlvybERqcieLHrRzKBra
   &nonce=2T1AgaeRTGTMAJyeDMN9IJbgiUG HTTP/1.1
  Host: server.example.com

Authorization Response は HTML Form を用いた POST リクエストにより返されます。

  HTTP/1.1 200 OK
  Content-Type: text/html;charset=UTF-8
  Cache-Control: no-cache, no-store
  Pragma: no-cache

  <html>
   <head><title>Submit This Form</title></head>
   <body onload="javascript:document.forms[0].submit()">
    <form method="post" action="https://client.example.org/callback">
      <input type="hidden" name="state"
       value="DcP7csa3hMlvybERqcieLHrRzKBra"/>
      <input type="hidden" name="id_token"
       value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw
         iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC
         Jpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5M
         DMxMTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1B
         SnllRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0F
         NTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2Mz
         kwMDg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqq
         wwq-Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fk
         IufbGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcq
         XVEKhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_h
         nrQ5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc
         0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/>
    </form>
   </body>
  </html>

最終的に、Client に送られるのは以下のようなPOSTリクエストになります。

  POST /callback HTTP/1.1
  Host: client.example.org
  Content-Type: application/x-www-form-urlencoded

  id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiwiYX
         VkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLCJpc
         3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5MDMx
         MTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1BSnl
         lRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTD
         oyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2MzkwM
         Dg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqqwwq
         -Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fkIuf
         bGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcqXVE
         KhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_hnrQ
         5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc0uV
         AwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA&
  state=DcP7csa3hMlvybERqcieLHrRzKBra

Response Mode についてなんとなくわかりましたでしょうか。 今回の仕様では、この辺りをさらに拡張するぞ!って話です。

JWT形式で Authorization Response を返すための Response Mode

仕様では Authorization Response に含まれる JWT の内容から先に定義されていますが、Response Mode のあたりから説明していきます。

この仕様で定義されている response_mode パラメータは次の4つです。

  • query.jwt : JWT形式の Authorization Response をクエリパラメータに指定する
  • fragment.jwt : JWT形式の Authorization Response をフラグメントに指定する
  • form_post.jwt : JWT形式の Authorization Response を HTTP POST で送る
  • jwt : Response Type に対応した Response Mode にて JWT形式の Authorization Response を返す
    • response_mode=jwt&response_type=code : response_mode=query.jwt
    • response_mode=jwt&response_type=(code, none以外) : response_mode=fragment.jwt

Authorization Response の例の前に、response_type パラメータ毎にどのようなJWTになるかを見ていきましょう。

JWT Response Document

JWTは以下のデータを必ず含みます。

  • iss : レスポンスを作成した Authorization Server の Issuer URL
  • aud : レスポンスの対象である Client の client_id
  • exp : JWT の有効期限

さらに、JWTはエラーレスポンスの場合でも Authorization Endpoint のレスポンスパラメータを含みます。 複数の Response Type の組み合わせに対しても適用されますが、この仕様では code, token について示します。 OIDC の Session Management のような拡張仕様で定義されているレスポンスパラメータもJWTに含まれます。

Response Type が "code" である時の Authorization Response

いわゆる Authorization Code Grant のレスポンスが JWT に含まれます。

  • code : Authorization Code
  • state : リクエストに含まれる state パラメータ

JWT の Payload は以下のようになります。

{
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "exp":1311281970,
   "code":"PyyFaux2o7Q0YfXBU32jhw.5FXSQpvr8akv9CeRDSd0QA",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw"
}

エラーレスポンスの時は JWT に RFC6749 の sections 4.1.2.1 で定義されているエラーレスポンスパラメータを含みます。

  • error
  • error_description (OPTIONAL) - 人間が読めるようなエラー内容
  • error_uri (OPTIONAL) - エラーの内容についてのURL
  • state

JWT の Payload は以下のようになります。

{
   "error":"access_denied",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw"
}

エラーレスポンスについては Client に戻すべきエラーとそうでないものがあるので、その辺りは今まで通り気をつける必要がありそうです。

Response Type が "token" である時の Authorization Response

Implicit Grant の場合は次のようになります。

  • access_token - Access Tokenの値
  • token_type - Access Token の種類。 "Bearer" など
  • expires_in - Access Token の有効期限
  • scope - Access Token の Scope
  • state

RFC6749 と照らし合わせると、Scope は オプションですかね。

{
   "iss":"https://accounts.example.com",
   "aud":"s6BhdRkqt3",
   "exp":1311281970,
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "state":"S8NJ7uqk5fY4EjNvP_G_FtyJu6pUsvH9jsYni9dMAJw",
   "token_type":"bearer",
   "expires_in":"3600",
   "scope":"example"
}

エラーレスポンスについては code の場合と同様です。

Signing and Encryption

上記のレスポンスパラメータはJWS で署名をつける もしくは JWE にて暗号化されます。 Authorization Server は 自分自身とClient の metadata を用いてアルゴリズムを決定します。

次は Response Mode 毎の Authorization Response を見ていきましょう。

Response Mode "query.jwt"

response というクエリパラメータとしてJWTの値が指定され、HTTP リダイレクトにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 302 Found
Location: https://client.example.com/cb?
response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLm
V4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTMxMTI4MTk3MCwiY29kZSI6IlB5eU
ZhdXgybzdRMFlmWEJVMzJqaHcuNUZYU1FwdnI4YWt2OUNlUkRTZDBRQSIsInN0YXRlIjoiUzhOSjd1cW
s1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkTUFKdyJ9.HkdJ_TYgwBBj10C-aWuNUiA062Amq
2b0_oyuc5P0aMTQphAqC2o9WbGSkpfuHVBowlb-zJ15tBvXDIABL_t83q6ajvjtq_pqsByiRK2dLVdUw
KhW3P_9wjvI0K20gdoTNbNlP9Z41mhart4BqraIoI8e-L_EfAHfhCG_DDDv7Yg

response_typetoken, id_token が含まれ、 JWE を"使わない"時、 URL からの漏洩を防ぐために query.jwt の値を使ってはいけません。

Response Mode "fragment.jwt"

フラグメント部に response というパラメータとしてJWTの値が指定され、HTTP リダイレクトにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 302 Found
Location: https://client.example.com/cb#
response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLm
V4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTMxMTI4MTk3MCwiYWNjZXNzX3Rva2
VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIsInN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdH
lKdTZwVXN2SDlqc1luaTlkTUFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMz
YwMCIsInNjb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z17v1Hyzf
Mqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2ifzRmqPoDdmJGaWZ3tnRyFW
NnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE

Response Mode "form_post.jwt"

response というHTML フォームの値としてJWTの値が指定され、User-Agent内で自動的に送信されることで HTTP POSTメソッドにより Client に送られます。 フォーマットは application/x-www-form-urlencoded です。

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-cache, no-store
Pragma: no-cache

<html>
 <head><title>Submit This Form</title></head>
 <body onload="javascript:document.forms[0].submit()">
  <form method="post" action="https://client.example.com/cb">
    <input type="hidden" name="response"
     value="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2
      FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTM
      xMTI4MTk3MCwiYWNjZXNzX3Rva2VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIs
      InN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkT
      UFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMzYwMCIsIn
      Njb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z1
      7v1HyzfMqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2i
      fzRmqPoDdmJGaWZ3tnRyFWNnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE"/>
    </form>
   </body>
  </html>

Client には HTTP POSTなリクエストが送られます。

POST /cb HTTP/1.1
  Host: client.example.org
  Content-Type: application/x-www-form-urlencoded

  response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2
      FjY291bnRzLmV4YW1wbGUuY29tIiwiYXVkIjoiczZCaGRSa3F0MyIsImV4cCI6MTM
      xMTI4MTk3MCwiYWNjZXNzX3Rva2VuIjoiMllvdG5GWkZFanIxekNzaWNNV3BBQSIs
      InN0YXRlIjoiUzhOSjd1cWs1Zlk0RWpOdlBfR19GdHlKdTZwVXN2SDlqc1luaTlkT
      UFKdyIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJleHBpcmVzX2luIjoiMzYwMCIsIn
      Njb3BlIjoiZXhhbXBsZSJ9.bgHLOu2dlDjtCnvTLK7hTN_JNwoZXEBnbXQx5vd9z1
      7v1HyzfMqz00Vi002T-SWf2JEs3IVSvAe1xWLIY0TeuaiegklJx_gvB59SQIhXX2i
      fzRmqPoDdmJGaWZ3tnRyFWNnEogJDqGFCo2RHtk8fXkE5IEiBD0g-tN0GS_XnxlE

Processing rules

Authorization Response を受け取った Client 側の検証処理について説明します。

  1. (OPTIONAL) JWT ヘッダパラメータの kid の値を用いてJWTをdecryptする
  2. state パラメータを取得して User-Agent との紐付けを検証(CSRF対策) state パラメータはワンタイムなCSRF対策トークンとして利用され、検証完了後破棄されます。
  3. iss の値を検証 (期待したものと一致するかどうか)
  4. aud の値を検証 (client_id と一致するかどうか)
  5. exp の値を検証
  6. iss, kid の組み合わせから署名を検証. 鍵周りの詳細はこの仕様の範囲外。

いずれの検証も失敗したらすぐにエラーを返します。 これらの検証が終わるまでは、Authorization Response の中身である codeaccess_token などの値を利用してはいけません。

Metadata

Client Metadata

Client 側で期待するJWTの署名や暗号化のアルゴリズムなどを定義できます。詳細は仕様みてください。

  • authorization_signed_response_alg
  • authorization_encrypted_response_alg
  • authorization_encrypted_response_enc

Authorization Server Metadata

Authozation Server 側でサポートしている値を定義できます。詳細は仕様みてください。

  • authorization_signing_alg_values_supported
  • authorization_encryption_alg_values_supported
  • authorization_encryption_enc_values_supported
  • response_modes_supported : サポートする response_mode パラメータ

OAuth Server Metadata についてはこちらのRFCを参照してください。

RFC 8414 - OAuth 2.0 Authorization Server Metadata

Security considerations

  • DoS using specially crafted JWTs : でかい JWK Set を処理する感じになってネットワークやCPUを圧迫するとか、JWK Set URL が DDos のターゲットみたいになるとか。鍵周りはよく考えて設計する必要がありますね。
  • Code Replay : 同一 Client, 別のユーザー向けの Authorization Code を含む攻撃は、state パラメータの検証で対応可能です。
  • Mix-Up : あるClientと複数のAuthorization Serverがやり取りする場合のMix-Up 攻撃については 図解:OAuth 2.0に潜む「5つの脆弱性」と解決法 (4/4):デジタルID最新動向(2) - @IT 見れば良いと思います。iss, aud の検証をしっかりやる必要があります。
  • Code Leakage : 暗号化すれば code の値も外から見えなくなります。

まとめ

  • Authorization Response に JWT を使う方法が定義されている
  • Response Type, Response Mode による挙動とJWTの中身の違いに注目せよ
  • JWT生成のところで鍵周りとかは仕様範囲外なので、よく考えて設計する必要がある

というところでしょうか。

もっと FAPI について知りたい?

ひたすら仕様を読む会ってのがあるみたいです。

peatix.com

今回紹介した JARM も入ってますが、既にあなたは「JARM完全に理解した」状態なので余裕ですね。 お時間とか色々と余裕がある方は是非参加してみてください。

ところで、"JARM" ってどう読むの??? ではまた。

文字で読みたい2分間OAuth講座 : (3) Where are Sender Constrained Tokens used?, (4) Why Refresh Token?

おはようございます!こんにちは!こんばんは! ritouです。

(2019/4/1 : Sender Constrained Token と表記する部分を User Constrained Token としていたので修正しました。ご指摘ありがとうございます!)

文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens - r-weblife の続きで、第3, 4回目について説明します。

(3) Where are Sender Constrained Tokens used?

www.youtube.com

第2回で登場した Sender Constrained Token ですが、OAuth 2.0のどこで使われているのでしょうか? RFC6749には出てこない?それでは見ていきましょう。

OAuth 2.0のフローのうち、最も使われているのは Authorization Code Grant と呼ばれるものです。 その中で出てくるトークン(的なもの)に Sender Constrained Token が含まれています。 ここからじゃんじゃんスクショ貼って行くぞ!

Authorization Code Grant

ユーザーは Web アプリケーションである Client のサービスをブラウザから使おうとします。

f:id:ritou:20180920003441p:plain

ブラウザは Client にリクエストを送ります。

f:id:ritou:20180920003427p:plain

Client は認可リクエストを生成します。

f:id:ritou:20180920003606p:plain

Client はブラウザのリダイレクトを利用してユーザーを Authorization Server に送ります。

f:id:ritou:20180920003631p:plain

Authorization Server はユーザー認証をします。

f:id:ritou:20180920003650p:plain

Authorization Server は Client が要求している内容をユーザーに示して、承認を求めます

f:id:ritou:20180920003709p:plain

ユーザーが承認のアクションをとります。

f:id:ritou:20180920003729p:plain

Authorization Server は Client (のID)に紐づいた"Authorization Code"と呼ばれる "Sender Constrained Token" を生成します。

f:id:ritou:20180920003752p:plain

Authorization Server はブラウザのリダイレクトを利用して、"Authorization Code" を含む認可応答を Client に返します。

f:id:ritou:20180920003809p:plain

Client は "Authorization Code"、Client ID、Secret を含むトークン要求を Authorization Server に送ります。 Clientは自らを認証するためにClient IDとSecretを含みます。

f:id:ritou:20180920003829p:plain

Authorization Server は、トークン要求に含まれる "Authorization Code" が正しい Client から送られてきたものか確認します。

"User Constrained Token" 的には、

  • Authorization Code に紐づく Client とトークン要求を送った Client が一致すること
  • Client ID / Secret の組み合わせが正しいこと

という確認をすることで正しいClientから送られてきたとみなします。

f:id:ritou:20180920003839p:plain

Authorization Server は "Access Token" と "Refresh Token" を返します。 ここで、RFC6749(+RFC6750の組み合わせ)の"Access Token"は、 "Bearer Token"、"Refresh Token" は "Sender Constrained Token" です。

f:id:ritou:20180920003850p:plain

Client は Access Token を利用してリソースアクセスを行います。あとはよしなに。

f:id:ritou:20180920003900p:plain

まとめ

第3回のまとめとしては、

  • OAuth 2.0 の Authorization Code Grant について説明した
    • その中で出てくる、"Authorization Code" と "Refresh Token" は いわゆる "Sender Constrained Token" である
  • 他に "Sender Constrained Token" の使いどころを知りたければ、"FAPI" で検索しましょう。

となります。

ここでなぜ Refresh Token の使い方を説明しないのかと思われるでしょう。 第4回は "Refresh Token" についての内容となります。

(4) Why Refresh Token?

www.youtube.com

Refresh Token は Authorization Server に対して"のみ"利用されるトークンです。 第4回では、OAuth 2.0 の Refresh Token がなぜ必要なのかについて説明します。

OAuth 2.0 の Access Token は、(scopeの概念を入れなければ)複数の Protected Resource に対して利用できます。

f:id:ritou:20180920103130p:plain

"Access Token" が "Bearer Token" であり、Protected Resource のいずれかがハックされて攻撃者の支配下に置かれた場合を考えます。

f:id:ritou:20180920103339p:plain

攻撃者はハックした Protected Resource にて取得した "Access Token" を用いて、別の Protected Resource にアクセスできます。

f:id:ritou:20180920103441p:plain

第3者に "Bearer Token" を使われてしまう、このような被害を"軽減"させるために、"Access Token" に短い有効期限を設定する方法があります。

有効期限を短く設定することで、頑張って "Access Token" を取得できた攻撃者が別の Protected Resource に利用しようとしてもエラーとなります。

f:id:ritou:20180920104200p:plain

しかし、Client が "Access Token" のみを保存して利用する場合、ユーザーに対して頻繁にリソースアクセスの承認を求める必要があり、ユーザーがオンライン状態でなければゲームオーバーです。オンラインだったとしてもメンドクセ。

この状況を改善するため、 "Refresh Token" が利用されます。 "Refresh Token" は Authorization Server だけに送られるトークンであり、通常は長い有効期限が設定されています。 Protected Resource をハックした攻撃者に "Refresh Token" が送られることはありません。 Client はこれを Authorization Server に送り、新しい Access Token を取得します。 Access Token を Refresh するためのトークンなので "Refresh Token" と言うわけです。 ちなみに、第3回で説明した通り "Refresh Token" は”Sender Constrained Token” なので、Client ID / Secret などと共に送られ、Authorization Server は検証を行います。

と言うことで、第4回は "Bearer Token" な "Access Token" を悪用されるリスクを軽減されるために、"Refresh Token" が使われるよっていうお話でした。

今回は動画をそのまま文字にするだけじゃなくて、色々補足しようと思ったら長くなりました。これが良いのかどうかはわからん... 次回予告です。

  • (5) Secret of Authorization Code : Authorization Code についての説明!?
  • (6) Actors of OAuth : 第1回よりもちょっと詳しく登場人物を説明!?

ではまた。

この投稿をみて、動画に興味を持っていただいた方は是非、チャンネルをサブスクライブしてください!!! www.youtube.com

文字で読みたい2分間OAuth講座 : (1) The Basic Concepts (2) Bearer and Sender Constrained Tokens

おはようございます!こんにちは! ritouです。

みんな知ってる Nat Sakimura 氏が YouTube で公開されている、いわゆる2分間OAuth講座ってのがあります。

www.youtube.com

英語で話されているものの、日本語字幕もあるし、とにかくわかりやすい。 しかし、世の中には変わった人もいるものです。 文章に落として説明してくれと頼まれましたので、2回ぶんずつ書いていくぞ!

(1) The Basic Concepts of OAuth

www.youtube.com

多くの人にOAuthは難しいと言われます。 しかし、地下鉄の乗り方がわかるなら、そんなに難しいことではありません。

OAuthとは、価値のある資源「Protected Resource」を保護するメカニズムです。

地下鉄に乗車することを考えてみましょう。 誰でもタダで乗れる訳ではありません。改札があります。 改札を通るためには、券売機で切符を買う必要があります。

OAuth では、券売機のことを Authorization Server, 切符のことを Access Token と呼びます。 Access Token は紙の切符のようにワンタイムのものや、定期のように有効期限が決められているものもありますが...とりあえずこれがあれば改札を通れます。 ということで、第1回でOAuthについて覚えておく必要があるのは

  • Authorization Server : 地下鉄の券売機
  • Protected Resources : 改札を通らなければ乗れない地下鉄
  • Access Token : 切符

の3点です。 次回は切符、つまり Access Token の種類について説明します。

(2) Bearer and Sender Constrained Tokens of OAuth

www.youtube.com

今回は2種類のAccess Tokenを紹介します。

  • Bearer Token : 第1回で説明した地下鉄の切符のようなトークン。所持していれば使えるので、他の人が拾っても使えます。
  • Sender Constrained Token : 利用者を制限するトークン。飛行機の搭乗券のように、利用者が指定されている、制限されているもの。

飛行機の搭乗券は通常、

  • 搭乗券の名前とパスポートに記載されている名前が一致している
  • パスポートの顔写真と搭乗者の顔が一致している

という2点を確認することで、搭乗者と搭乗券が紐づきます。

OAuthの場合、そのような紐付け、確認のために暗号鍵を利用します。 トークンに紐付く鍵を持っている必要があることから、"Holder of Key Token" とも呼ばれます。

OAuth 2.0では、これら2種類のトークンを扱えます。

リスクの低いユースケースではBearer Tokenが使われ、銀行取引などではUser Constrained Tokenが使われます。

こんなところでしょうか。

元がシンプルなやつを落とそうとすると結構時間かかりますね。 次回は(3), (4)について書きます。もうちょっと補足を増やすのが良さそう。

ではまた。

この投稿をみて、動画に興味を持っていただいた方は是非、チャンネルをサブスクライブしてください!!! www.youtube.com

builderscon tokyo 2018 でパスワードレスについて話しました & idcon に行こう

(ふと描き忘れたなというところを追記してます)

どこで何を話したのか

builderscon tokyo 2018 にてお話しして来ました。

builderscon.io

資料もアップロード済みです。

内容は以下について緩く話して来ました。

  • パスワード認証 : そもそも人類には無理そう
  • あるある実装 : パスワード認証 + リカバリー、最近多要素認証。ソーシャルログインはパスワードとセットになってる。
  • WebAuthn : 細かい生体認証とかを頑張るのは Authenticator なので、導入するサービスは大枠の仕組みを実装すれば良い
  • パスワードレスに向かうためには : パスワード認証に依存している部分を意識すれば今ある認証方式でもいける

最初から難しい話をするつもりはなく、パスワード認証やめようってなったときに自分はこう考えて言ったよっていうのをベースに、新しい技術の話も聞きたいでしょ。でも昼過ぎに細けぇ話したらみんな寝るだろやめとくわみたいなところでした。 台本用意してたはずが飛んで時間が早く終わってしまったので、何個か質問してもらいました。WebAuthn 関連が多かった気がします。

  1. Authenticator管理などのプラクティスについて : USBのAuthenticatorを無くした時や、複数の環境でいろんなAuthenticator使いたい時のカオス感など。細かいところは色々決まってなさそう
  2. OAuth 2.0 / OpenID Connect との関係 : WebAuthn は IdPがユーザー認証するところで使われるイメージ
  3. idcon について : ガチな参加者はいるか?いる

質問の内容、特に後半は明らかに気を使ってもらってる感もありますが、1の少し実践的な運用のあたりに興味を持ってもらったということは、未来は明るい気がしました。

今回のお話を聞いて、少しでもID周りや最新の認証方式に興味を持ってもらえたらいいなというところで、idcon やってる、idconに来いというのを最後に書いときました。

ということで、idconの告知をせねばなるまいとこのブログ記事を書いたわけです。

idcon に行こう

良いタイミングで次の idcon やるってのを、聞きました(運営してる詐欺) いや、発表当日にこの募集してあったら完璧だったと思いますけれども。

fido2-workshop.connpass.com

内容も FIDO / WebAuthn で、中身も参加者もガチです。 私の一押しはやはりGooglerなあの人かなっ!

質問したら期待以上の答えが帰ってくること間違い無いでしょう。 よくみたらもう満席ですが、ぜひご参加ください。

ついでに

放置してたブログも引っ越しました。 技術的な投稿はQiitaに書く予定ですが、告知みたいなのはこっちで。 次はいつ書くのか、それは知らん。

ではまた。

OpenID Connectのセッションに関する3つの仕様について

おはようございます、ritouです。

OpenID Summit Tokyo 2015までもうちょいですね。
プログラムを見てみましょう。
OpenID Summit Tokyo 2015
仕様の話もありそうですね。
今回はRFCXXXXとして発行された系ではなく、OpenID Foundationで策定が進んでいるOpenID Connect関連の仕様を紹介します。
ドラフト仕様も、いろいろあります。
Specifications | OpenID

このうち、今回は次の3個の仕様をとりあげます。

  • Session Management – (Optional) Defines how to manage OpenID Connect sessions, including postMessage-based logout functionality
  • HTTP-Based Logout – (Optional) Defines an HTTP-based logout mechanism that does not use an OP iframe on RP pages
  • Back-Channel Logout – (Optional) Defines a logout mechanism that uses back-channel communication between the OP and RPs being logged out

この説明だけ見ると、みんなlogout言ってます。
これらがどう違うのかを確認していきましょう。

OpenID Connect Session Management 1.0 - draft 25

Draft: OpenID Connect Session Management 1.0 - draft 28

ざっくり書くと、

  • RPはAuthN Response(OAuthでいうところのAuthZ Response)でセッションに紐づく、session_stateという値を受け取る
  • RPはiframe + postMessageをつかって、OPにsession_stateを送り、状態変更があったかどうかを問い合わせる
  • RP始動なログアウトのときは、OPのログアウト用URL(end_session_endpoint)にリダイレクトする

という感じです。

そういえばずっと前に記事を書いた気がします。

ritou.hatenablog.com

この時点から、そんなに変わってないです。

OpenID Connect HTTP-Based Logout 1.0 - draft 03

Spec obsoleted by openid-connect-frontchannel-1_0

(Session Managementに対して)RP上にiframeを必要としないHTTPベースのログアウトメカニズムを定義するとあります。

  • RPはログアウト用エンドポイントとセッションIDが必須かどうかをOPに登録、OPからiframeでそのエンドポイントを呼び出されたらログインセッションをクリアする
  • OPはログイン状態のRPを"visited site"Cookieのようなもので管理しておき、ログアウト時にiframeでそれぞれのログアウトURIを呼び出すことでRPに通知する
  • Session Management同様、RP始動なログアウトのときは、OPのログアウト用URLにリダイレクトする

ということで、Session Managementと並行して実装することもできます。
iframe対応をしなくてもOPの状態変更を知ることができるのは便利そうですね。

OpenID Connect Back-Channel Logout 1.0 - draft 01

Draft: OpenID Connect Back-Channel Logout 1.0 - draft 04

HTTP-Based Logoutとは異なり、OP-RP(複数の場合もあり)間のバックチャンネルコミュニケーションを用いてログアウトさせます。
Session Managementなんかはiframeで問い合わせるページをユーザが開いていなければチェックができないなどUser-Agentがアクティブである必要がありますが、バックチャンネルだと気にしなくていいです。
その代わり、CookieとかHTML Strageとかでさくっと実装できる感じではないので、サーバサイドでセッションを切るような作りにしなければとか、よくあるFirewall/NATの話とかがIntroductionに書いてあります。

定義されているものは

  • OPはログイン状態のRPを覚えておく必要がある(セッションに対して、ユーザーとRP一覧を紐づけておく)
  • ID Tokenに似ているLogout Tokenと呼ばれるものがバックチャンネルで送られる。Logout Tokenのsub/sidあたりを
  • 上記2つの仕様と同様、RP始動なログアウトのときは、OPのログアウト用URLにリダイレクトして、OPに行った後はそのあとバックチャンネルの処理になる

という感じです。詳細は長くなるので省略します。

気になるこまけぇこと

  • Session StateでAuthN Responseで受け取るユーザのログイン状態はsession_state
  • 他の2つのLogout関連の仕様では、セッション識別子はsidとしてID Tokenに含まれる

用途としてはたしかに別なので、こうなる気持ちはわかります。
が、一般的にユーザがログアウトしたらセッションIDを破棄すべきという話もあるので、session_stateとsidは統一できそうな気がします。
渡し方については...これはちょっと考えないといけませんね...

まとめ

  • OpenID Connectのセッション関連の仕様は3つある
  • Session Management : iframeを用いたRP->OPのログイン状態の問い合わせ方とRP始動ログアウト時のOPへのリダイレクト
  • HTTP-Based Logout : RPはOPからiframeで対象エンドポイントが呼び出されたらログアウト処理
  • Back-Channel Logout : RPはバックチャンネルでlogout tokenを受け取ったらログアウト処理

なんとなく違いがおわかりいただけたでしょうか。
このあたりは、時間を見つけてデモ環境を作りたいと思います。

ではまた!

RFC7662として発行されたOAuth Token Introspectionとは

こんばんは、ritouです。
今回紹介する仕様は、RFC7662 OAuth Token Introspectionです。

ざっくり言うと

Token Introspectionとかいうと、Token置換攻撃対策としてのClient-AuthZ Server間のAccess Token検証を想像する方もいるかもしれませんが、この仕様はそれとはあまり関係ありません。

この仕様はOAuthのProtected Resourceのための仕様です。
RFC 6749 - The OAuth 2.0 Authorization FrameworkのRoleのところには、resource serverがprotected resourceをホストしていて、 access tokenを用いたリクエストを受ける、とあります。
簡単に言うと、APIサーバのことですね。

AuthZ ServerとResource Serverが分かれることはよくある話です。
小規模なところは、裏で同じDBを見に行くなどしてAccess Tokenの検証をすればよいわけですが、そこそこの規模のサービスだとか、ID統合など諸々の理由でそういうつくりができないこともあるでしょう。
また、最近はなんたらAPI Gatewayみたいなやつが最初にリクエストを受け、Resource Serverに来る前にAccess Tokenの検証ができるみたいな構成も聞きます。
この仕様はそういうのではなく、"Resource Serverが受け取ったAccess Tokenの状態をAuthZ Serverに問い合わせる"ような状況において、Access Tokenの送り方(問い合わせ方)を定義したものです。

ここからは、RFCの構成を意識しながら説明していきます。

課題

Introductionに書いてあります。

  • OAuth 2.0の仕様で、Access Tokenは、"The string is usually opaque to the client."となっている
  • Protected Resourceの処理には、Tokenが有効かどうかや紐づいたユーザー情報、認可に関する情報などが必要である
  • しかし、OAuth2.0ではProtected Resourceがそれらを知る方法が具体的に定義されていないので、JWTとか独自の内部処理とかでの対応が必要となる(ので、例えばこんな提案とかが出回るわけです→OAuth 2.0のAccess TokenへのJSON Web Token(JSON Web Signature)の適用 - r-weblife)

この仕様が定めること

  • Protected ResourceがClientから受け取ったTokenのメタデータをAuthZ Serverに要求する方法
  • メタデータはTokenの有効性や許可されたアクセス範囲、認可情報などを含む
  • Token文字列自体がメタデータを含んでいるかどうかにかかわらず、この方法を利用できる

Protected Resourceが扱うのはAccess Tokenですが、仕様上はAccess TokenだけではなくRefresh Tokenでもいいみたいです。

Introspection Endpoint

AuthZ ServerはProtected Resourceからの要求を受けるエンドポイントを用意する必要があります。

AuthZ ServerがProtected Resourceにこのエンドポイントをどうやって教えるかなどは仕様の対象外です。
まずはリクエストの説明からです。

Introspection Request

Protected Resourceは"application/x-www-form-urlencoded"形式で次のパラメータを持つHTTP POSTなリクエストを送ります。

token scanning attackを防ぐため、このエンドポイントにアクセスするためのProtected Resource - AuthZ Server間の認可が必須です。
この認可については仕様対象外なので何でもいいんですが、OAuth 2.0の仕様に準拠させた例が載っています。
説明すると逆に混乱しそうですが、"AuthZ Serverにとって、Protected ResourceはIntrospection endpointにアクセスしてくるClientである"という感じです。

# client authenticationと組み合わせた例
POST /introspect HTTP/1.1
Host: server.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

token=mF_9.B5f-4.1JqM&token_type_hint=access_token

# bearer tokenを用いる例
POST /introspect HTTP/1.1
Host: server.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer 23410913-abewfq.123483

token=2YotnFZFEjr1zCsicMWpAA

次はレスポンスの説明です。

Introspection Response

レスポンスはJSON("application/json")形式です。

HTTP/1.1 200 OK
Content-Type: application/json

{
 "active": true,
 "client_id": "l238j323ds-23ij4",
 "username": "jdoe",
 "scope": "read write dolphin",
 "sub": "Z5O3upPC88QrAjx00dis",
 "aud": "https://protected.example.net/resource",
 "iss": "https://server.example.com/",
 "exp": 1419356238,
 "iat": 1419350238,
 "extension_field": "twenty-seven"
}

必須なのは、activeという値(boolean)で、Tokenが有効かどうかが入ります。
それ以外で気になるのがあったら仕様を読みましょう。
JWTのClaimについてはRFC 7519 - JSON Web Token (JWT)を読むといいでしょう。

Security Considerationsも流し読みしておきます。

Security Considerations

  • AuthZ ServerのTokenチェックをしっかりやろう
  • 前述したような攻撃を防ぐために、Protected Resourceの認証、Introspection Endpointへアクセスできるかどうかの確認をしっかりやろう
  • TLS/SSLのサーバーチェックとかしっかりやろう
  • AuthZ Serverのサーバーサイドのログからの漏えいを防ぐため、GETじゃなくPOST
  • キャッシュすると無効化の同期が...
  • Token文字列が構造化されてたらどうこう...

みたいなことが書いてあります。
実際こういうしくみを作ろうと思うと、負荷のあたりは悩ましげな感じですねー。

Privacy Considerations

レスポンスにセンシティブなプライバシー情報を含む場合があるのでほげほげ。

まとめ

  • Protected Resource(を持ってるResource Server)からAuthZ ServerへのToken情報の要求方法が定義されている
  • HTTP POSTのリクエストでTokenを送るときに、Protected Resource - AuthZ Server間の認証/認可が必要
  • レスポンスに"Tokenが有効かどうか"は必須

個人的には...小規模だとなかなか使わないし、すごい大規模になると負荷とかでもっと工夫しそうなので、異なる企業間をまたいだ疎結合なシステムのときにAPIでつなぐのかなーとか想像しちゃうところです。
ではまた!

RFC7636として発行されたOAuth PKCEとは

おひさしぶりです、ritouです。

今日は家で風邪治してましたが、TLに流れてきた次世代なんちゃらの話題に乗っかって、ざっくりとした仕様紹介です。

一言でいうと

この規格はOAuth 2.0 [RFC6749]のPublic Client の Code Interception Attack 脆弱性に対応するもので、ephemeral keyを生成して、これを使ったProof of Possession of Key をします。

OAuth PKCEがRFC7636として発行されました。 | @_Nat Zone

なんですが、RFCの構成を意識して書いていきます。

問題

RFCでは、Introductionに書いてあります。

OAuth 2.0 [RFC6749] public clients are susceptible to the authorization code interception attack.

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

Public Clientってのは、client_secretを安全に管理できないclientですね。
そういうclientはimplicitでは?って思われる人もいるかもしれませんが、落ち着きましょう。
Public ClientがAuthZ Codeを使う場合、次のようなフローになります。

  1. Clientは外部ブラウザを立ち上げるなりして、AuthZ Reqを送る
  2. AuthZ Serverに送られ、ログインとか何とかした後にAuthZ CodeがブラウザからClientに返される
  3. Clientは、AuthZ CodeとClient CredentialをAuthZ ServerのToken Endpointに送って、Access Tokenを取得する

これでPublic Clientの場合にどこがどうなっててどう危ないか、後ろから見ていくとわかるものです。

  • Access Tokenを取得するためには、AuthZ CodeとClient Credentialが必要!
  • Public Clientなので、Client Credentialを知ることができる。つまり、AuthZ CodeがあればAccess Tokenを取得できる!
  • もし、カスタムURIスキームとか使ってAuthZ Codeの受け渡しのときに攻撃者にAuthZ Codeが渡ったら...AccessTokenを取得される問題!

ということですね。

解決方法

こちらもIntroductionに書いてあります。

To mitigate this attack, this extension utilizes a dynamically
created cryptographically random key called "code verifier". A
unique code verifier is created for every authorization request, and
its transformed value, called "code challenge", is sent to the
authorization server to obtain the authorization code. The
authorization code obtained is then sent to the token endpoint with
the "code verifier", and the server compares it with the previously
received request code so that it can perform the proof of possession
of the "code verifier" by the client. This works as the mitigation
since the attacker would not know this one-time key, since it is sent
over TLS and cannot be intercepted.

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

ここでは翻訳したいわけじゃないので...

  • Clientはcode verifierっていう、動的に生成されるcryptographically random key(←妥協)を生成
  • code verifierは毎回AuthZ Req毎に生成される
  • Clientは(code verifierを変換した)code challengeというパラメータをAuthZ Reqに含み、AuthZ ServerのAuthZ Endpointに送る
  • AuthZ ServerはAuthZ Codeとcode challengeを紐づけつつ、Clientのredirect_uriにAuthZ Codeを送る
  • Clientは、AuthZ Codeとcode verifierを一緒にAuthZ ServerのToken Endpointに送る
  • AuthZ ServerはAuthZ Codeと紐づけておいたcode challengeとcode verifierの値を検証し、Access Tokenを返す
  • 攻撃者はcode verifier知らないのでAuthZ Codeを奪うだけではAccess Token取得できない

こんな感じです。

ここまでで大事なことは書いたので、後はざっと流しましょう。

用語

  • code verifier : 動的に生成されたcryptographically random stringであり、Token EndpointへのAccess Token Reqに含まれる
  • code challenge : code verifierから生成されたchallengeの値で、AuthZ EndpointへのAuthZ Reqに含まれる
  • code challenge method : code verifierからcode challengeを作る時の方法
  • Base64url Encoding : 省略

code verifierの生成

最小43文字、最大128文字の...

code_verifier = high-entropy cryptographic random STRING using the
unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
from Section 2.3 of [RFC3986], with a minimum length of 43 characters
and a maximum length of 128 characters.

ABNF for "code_verifier" is as follows.

code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

code challengeの生成

code challengeの生成方法について、plainとS256が記載されています。

plain
code_challenge = code_verifier
S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

ABNF for "code_challenge" is as follows.

code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

ClientはS256できるならS256使う。どうしようもなかったらplain。

AuthZ Reqへの追加パラメータ

code challengeとmethodを指定します。

code_challenge
REQUIRED. Code challenge.

code_challenge_method
OPTIONAL, defaults to "plain" if not present in the request. Code
verifier transformation method is "S256" or "plain".

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

AuthZ Resについて

AuthZ ServerはAuthZ Codeとcode_challenge, code_challenge_methodは返してはいけません。
method=plainだったらcode verifierそのものですからね。

Access Token Reqへの追加パラメータ

code_verifier
REQUIRED. Code verifier

RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients

AuthZ Serverの検証について

  • code_challenge_methodの値に従って、code_verifierそのままもしくはs256した値とcode_challengeの値を比較する

Security Considerations

は、読んでおきましょう(疲れた)

まとめ

  • Public ClientからAuthZ Code使ってAccess Tokenを取得する場合のお話
  • Clientは毎回動的にcode verifierを生成する
  • これ使うと、redirect_uriの扱いが不安なPublic Clientでも、安全にAccess Tokenの取得が行えそう

文章だけでまだわからんので絵が欲しい って場合は、この記事をTweetしたついでに一言いただけると対応できるかもしれません。
ではまた!

Auth0のドキュメントに書いてあったMQTTをWeb APIっぽく使うための工夫

どーも、ritouです。

一言でいうと、MQTTでもTokenを使えそうというだけの話

MQTTに関して、様々な言語のライブラリや、他の機能と連携するプラグインなどもよく見かけます。
詳細な解説記事からさくっと使って「簡単でした!」みたいな記事までたくさん出回っておりますが、その中で、MQTTのAuthN&AuthZに着目した内容に触れているドキュメントを見つけました。
https://auth0.com/docs/scenarios/mqtt

Auth0ってのはIdentity as a Service、クラウドな認証基盤なサービスなんて紹介されたりしてるサービスです。
で、上記のドキュメントは”Authenticating & Authorizing Devices using MQTT with Auth0” というタイトルのとおり、ざっくり言うと

  • CONNECT時のusername/passwordとAuth0を用いて接続元デバイスの検証を行う = Authentication
  • PUB/SUB時にAuth0を用いて接続元デバイスとTopicの組み合わせの検証を行う = Authorization

という感じで、2種類の方法が紹介されています。

  • (1) MQTT Clientは普通の使い方、MQTT BrokerがAuth0に問い合わせる

  • (2) MQTT ClientはAuth0からTokenを取得し、"username/password"のところに"'jwt'/(取得したToken文字列)"を指定し、BrokerはTokenの検証を行う

(2)は個人的に大好物であるところのOAuthなWeb APIっぽい使い方に見えます。
username/passwordのところに違う値をぶっこむというやや強引なやり方ですが、Tokenの検証などについては前に書いたやつと同じ感じの設計でいけそうです。
OAuth 2.0のAccess TokenへのJSON Web Token(JSON Web Signature)の適用 - r-weblife

Moscaはこのあたりを試すのに便利

そもそもMQTT Brokerをしっかり実装しようとすると大変そうだし、さらにTokenを使うように拡張なんて...と思われるのはないでしょうか。
Auth0は、Node製のMQTT BrokerであるMoscaを利用しています。
Moscaでは、AuthN/AuthZに関連する機能を拡張しやすいつくりになっています。

With Mosca you can authorize a client defining three methods.

  • #authenticate
  • #authorizePublish
  • #authorizeSubscribe

Those methods can be used to restric the accessible topics for a specific clients.

https://github.com/mcollina/mosca/wiki/Authentication-&-Authorization

使い方はAuth0Moscaのサンプルを見るとわかりやすいです。

var auth0 = new Auth0Mosca(...);

//Setup the Mosca server
var server = new mosca.Server(...);

//Wire up authentication & authorization to mosca
server.authenticate = auth0.authenticateWithJWT();
server.authorizePublish = auth0.authorizePublish();
server.authorizeSubscribe = auth0.authorizeSubscribe();

Productionで使うとなると「接続/Pub数の制限とか、もっといろんな独自処理を挟みたい vs パフォーマンスがー問題」とか、いろいろ出てくるんでしょうけども、ちょっと試すには便利そうです。

まとめ

  • Auth0が出しているMQTT利用シナリオにあった、Tokenを用いる方法がWebAPIっぽいなーと思って紹介した
  • そのあたりの拡張が簡単にできるMoscaは、ちょっと遊ぶには便利そうな印象

そういえば、BaaSにもサーバーサイドスクリプトってのがあります。
MQTT as a Serviceを提供するようなところでもこのあたりの拡張ができて自サービスで発行したOAuthのAccess Tokenなどが使えると、MQTT Broker自体のパフォーマンスを下げずに既存のサービスとの親和性も高められてさらに便利かなーと思いました。

ではまた!