ネイティブアプリで OAuth 2.0 を安全に使うための OAuth 拡張

(新元号が発表されましたね。いらすとや さん仕事早い.....!)

新社会人・学生の皆さま、御入社・御入学おめでとうございます!

はじめまして。プラットフォーム事業本部の Kikuchi です。 普段は Cloud IoT OS のアカウント管理・認証・権限管理周りの機能検討や設計・開発をやっています。 主な開発言語 は Rust ではなく Ruby です。 Object#tap とか可愛いですよね。

さて、少し前のことですが、OpenID TechNight #16 ~ OpenID Connect 5周年記念 というイベントで、「OPTiM サービスでの OAuth 2.0/OpenID Connect と周辺技術の活用事例」というテーマで Lightning Talks をさせていただきました。

LT ということもあり時間が限られていたため、今回はネタ落ちした内容をご紹介していこうかと思います。

OAuth 2.0 に潜む脆弱性

Cloud IoT OS では、OAuth 2.0 を使って API を保護しています。 OAuth 2.0 (RFC6749) には、4つの認可フローが定義されていますが、OAuth 2.0 で一番良く使われるのは Authorization Code Grant と呼ばれる認可フローです。

OAuth 2.0 Authorization Code Grant のシーケンス図

OAuth 2.0 は Webサービスでの利用を基本としたデザインとなっているため、スマホアプリのようなネイティブ向けのアプリケーションでそのまま利用すると、Authorization Code Interception Attack (認可コード横取り攻撃) と呼ばれる攻撃が成立してしまいます。

認可コードの、横取り

通常のWebアプリであれば、Authorization Code (認可コード) はブラウザを介して直接 Client に対して到達しますが、ネイティブアプリの場合、ブラウザから離れ、OSを介してアプリに到達します。

ブラウザからアプリに遷移するときの方法としては、通常、Custom URL Scheme と呼ばれる方法が多く用いられています。 特定の URI を OS に登録しておくことで、OS がその URI へのアクセスを検知すると、アプリの任意のアクションを呼び出す仕組みです。

しかし、この Custom URL Scheme を OAuth 2.0 の Redirect URI として用いるには、重大な欠陥があります。複数のアプリケーションで、同じ値を登録出来てしまうことです。

Authorization Code Interception Attack のシーケンス図

攻撃者は、狙っているアプリケーションと同じ Custom URL Scheme を持ったアプリをユーザーにインストールさせ、利用させることで、比較的容易に認可コードを横取りできてしまうのです。

OAuth 2.0 では、Client の認証情報に対して容易にアクセスできる機密性の低いアプリケーション(ブラウザ上で動作するアプリや、ネイティブアプリ)のことを Public Client と呼んでいます。

Twitter の公式クライアントから Consumer Key & Consumer Secret が流出して(※ あれは OAuth 1.0a ですが)、一時期騒ぎになったことをご存知の方も多いのではないでしょうか。 Twitter のように、Public Client では、認証情報が比較的容易に漏洩しうるため、どうしてもクライアント認証があまり意味を成さない状態になってしまいます。 そのため、正規の Client の認証情報を手に入れることができる状態で、Authorization Code を横取りできてしまうと、本来のアプリの代わりに Access Token を受け取り、API にアクセスできてしまいます。

このような技の組み合わせで発生する攻撃が、認可コード横取り攻撃です。

Proof Key for Code Exchange by OAuth Public Clients (OAuth PKCE) とは?

この問題を解決するために、 Proof Key for Code Exchange by OAuth Public Clients (OAuth PKCE) と呼ばれる拡張仕様が、RFC 7636 で策定されています。 OAuth PKCE は、Authorization Code Grant のやり取り中のパラメータに code_verifiercode_challenge と呼ばれる2つの値を追加します。 それぞれ、以下のような役割となっています。

  • code verifier
    • Authorization Request と Token Request を紐付ける役割
    • 43〜128文字のURL Safe な文字で、高いエントロピーをもつランダムな値を生成して指定する
    • 認可リクエスト毎に異なる値を用いること
  • code challenge
    • Authorization Request 時に送信される code verifier を、Token Request 時に検証するための チャレンジ値
    • code verifier の ASCII String に対して、SHA-256 の digest 値を作成し、URL-Safe な Base64文字列に変換した値 (code_challenge_method=S256 の場合) を指定する

Ruby のコードで書くと、このように生成することが出来ます。

require 'securerandom'
require 'openssl'

code_verifier = SecureRandom.hex(32)
code_challenge = Base64.urlsafe_encode64(OpenSSL::Digest::SHA256.digest(code_verifier), padding: false)

これらの値を、Auhotization Code Grant のシーケンスに加えてみましょう。

OAuth 2.0 Authorization Code Grant with PKCE のシーケンス図

攻撃者の Client は、正規の Client が生成した code verifier の値を知らないため、横取りした Authorization Code を Token Request で Access Token と引き換えることは出来ません。 このように、OAuth PKCE は Authorization Request を発行したクライアントと、Token Request を発行したクライアントが同一であることを保証することで、攻撃を防ぐ仕組みを提供しています。

あとがき

OAuth PKCE と呼ばれる仕様は、認可コードの横取り攻撃に対する防御策となる仕様で、ネイティブアプリのようなユースケースでエンドユーザーを守る重要な仕組みです。 また、巷で話題の FAPI (Financial-grade API) と呼ばれる OAuth 2.0 のプロファイルでも、実装が必須とされている仕様となっています。 Cloud IoT OS のみならず、OAuth 2.0 を使ったアプリケーションを作っている方は、簡単に対応できるはずなので、ぜひ対応しておきましょう!

オプティム では、Web API を作る側・使う側どちらの立場の業務も行っています。 Web API エコノミーを作る仲間をいつでも募集していますので、興味のある方は是非一度お話しましょう!

www.optim.co.jp