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したついでに一言いただけると対応できるかもしれません。
ではまた!