Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

OAuth2 PKCE対応について

かとじゅん(@j5ik2o)です。OAuth2を使った、チャットワークAPI開発者向けのリリース情報です。

今回は、RFC7636 PKCE(=Proof Key for Code Exchange by OAuth Public Clients)をリリースしました*1。 ただ、API ドキュメントの修正が間に合っていないので、後追いで修正させていただきます。また、現状のPKCEの実装には一部制限がありますので、開発者の方は最後までこの記事に目を通していただけると助かります。

PKCEとは、認可コード横取り攻撃対策のためのOAuth2の拡張仕様です。詳しくは以下のブログ記事などを参考にしてください。

qiita.com qiita.com

簡単に説明すると、Authorization Code Flowで「ブラウザ上のJSやモバイルアプリなど」のパブリッククライアント*2を認可させるための拡張仕様です。つまり、OAuth2クライアントとしてのサーバなしで、ブラウザやモバイルアプリなどの端末上で動作するアプリケーションが、直接的にOAuth2クライアントとして実装できます。

Authorization Code Flow + PKCE

ところで、パブリッククライアントと聞くと、Implicit Flow*3とイメージするかもしれません。このフローではリフレッシュトークンも払い出されないので、そのときに取得したアクセストークンの寿命が尽きると、クライアントは再度認可を受ける必要があります。Authorization Code Flowより使い勝手は制限されます。

とはいえ、パブリッククライアントとAuthorization Code Flowの組み合わせでは、正規のカスタムスキームで起動する攻撃者のアプリによって、認可コードが横取りされる可能性があります。今回の対応したPKCEはAuthorization Code Flowに組み合わせて利用します。そうすることで認可コード横取り攻撃を対策でき、パブリッククライアントでも使い勝手のよいAuthorization Code Flowを利用できます(もちろん、PKCEはコンフィデンシャルクライアント*4でも利用できます)。

現状のPKCEの制約

現状、登録できるクライアントの種別はコンフィデンシャルクライアントのみで、肝心のパブリッククライアントはまだ対応していません…。そもそも登録できるクライントはコンフィデンシャルクライアントに限られているためです。パブリッククライアントを登録できるように対応を進めていきますので、今しばらくお待ちください。PKCEを利用できるのはコンフィデンシャルクライアントのみになりますが、ぜひお試しください。

PKCEの使い方

それではPKCEの利用方法を簡単に説明します。PKCEは管理画面上のクライアント情報に特別な設定は不要です。変更するのは認可リクエストとトークンリクエストのパラメータです。それらのパラメータは以下のとおりです。

  • code_verifier
    • [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" を含む文字列(43から128文字)
  • code_challenge
    • [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" を含む文字列(43から128文字)
    • 計算方法は BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
  • code_challenge_method
    • SHA256のみをサポートしているので、"S256"を指定

まず、PKCEを利用したい場合は、認可リクエストに必ずcode_challengecode_challenge_methodの両方を指定してください。また、両方もしくは片方でもパラメータが欠けると、PKCEを利用しない、通常の認可リクエストとして処理されるので注意してください。 code_challengeは、上記に記載したとおり、事前に生成したcode_verifierから計算してください。この値はトークンエンドポイントにリクエストするまで秘匿してください。通常のウェブアプリケーションであれば、クッキーに紐付くセッションオブジェクトに格納するとよいでしょう*5

https://www.chatwork.com/packages/oauth2/login.php?
response_type=code
&redirect_uri=https://example.com/callback.php
&client_id=Lvo0YN92ga5kP
&state=811435b3683ae95c1cf3197deaf1bfe4b411f587
&scope=rooms.all:read_write%20users.profile.me:read
&code_challenge=jlkGAsNvHshJNC7uXSSmC2tALONajPdupVf3TScb7zk
&code_challenge_method=S256

認可リクエストが正常に処理され、リダイレクト経由でトークンエンドポイントを叩く際に、code_verifierをトークンリクエストのリクエストボディに含めてください。 クッキーに紐付くセッションオブジェクト内に存在する場合は、その値をクエストパラメータに付与してください。

$ curl  -v --user 'Lvo0YN92ga5kP:secret' \
-X POST -d 'grant_type=authorization_code \
&code=26d13798facc9a0ca05a8cb7246020f15a311 \
&redirect_uri=https://127.0.0.1/callback \
&code_verifier=5b0029bd34e559e0abe7a37051aa411398913fc3579e27bd963a2b9a647f12f58a335beeb4d83a53a74ff1a6f99f6af385d2992c73beead39f57dcee95e0f954'  \
https://oauth.chatwork.com/token

クライアント認証と認可コードの正当性に加えて、code_verifierが正しくなければ、アクセストークンおよびリフレッシュトークンは払い出されません。つまり、認可コードが漏洩したとしても、code_verifierは認可リクエストした正規のクライアントだけが知っているので、攻撃者が認可を横取りすることができなくなります。また、認可リクエスト時にcode_challengecode_challenge_methodを指定しない場合は、従来どおりcode_verifierなしでトークンを取得できます*6

ということで、簡単でありましたが、PKCE対応のお知らせでした。ぜひ、お試しください! ご不明点・お気づきのことなどがあれば、フィードバックからお問い合わせください。

*1:PKCEはピクシーと呼ぶそうです

*2:クライアントシークレットを安全に秘匿できない環境で動作するOAuth2クライアントのこと。詳しくはRFC 6749 - The OAuth 2.0 Authorization Frameworkを参照してください

*3:Implicit Flow - RFC6749 The OAuth 2.0 Authorization Framework

*4:クライアントシークレットを安全に秘匿できる環境で動作するOAuth2クライアントのこと。詳しくはRFC 6749 - The OAuth 2.0 Authorization Frameworkを参照してください

*5:クッキー自体がセキュアであるという前提です

*6:パブリッククライアントではPKCEが必須になる可能性があります