API認証フロー Resource Owner Password Credentials Grantを自社モバイルアプリで利用する

目的

  • 自社アプリのような信頼できるモバイルアプリケーションからAPIを利用したい
  • モバイルアプリからAPIを利用する際のセキュリティ脆弱性は必ず排除する必要がある。

前提

  • APIは既に存在し自社webアプリケーションから利用されている。webアプリからの認証には client credentials認証フローを採用している。
  • REST API
  • APIリクエスト時には、アクセストークンを必須としている。アクセストークン未発行の場合アクセストークンの発行を行う必要がある。
  • 第3者へのAPI公開はまだ考えていないため、サードパーティーアプリからの認証は考えなくて良い。
  • なるべく時間はかけたくない

やったこと

OAuth 認証フローについて調べてみると、RFC文書内に認証フローは4種類定義されていおり、その中の Resource Owner Password Credentials GrantAPI認証フローとして今回の要件をみたす形で利用できる判断し実装した。

Resource_Owner_Password_Credentials_Flow

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow


   The flow illustrated in Figure 5 includes the following steps:

   (A)  The resource owner provides the client with its username and
        password.

   (B)  The client requests an access token from the authorization
        server's token endpoint by including the credentials received
        from the resource owner.  When making the request, the client
        authenticates with the authorization server.

   (C)  The authorization server authenticates the client and validates
        the resource owner credentials, and if valid, issues an access
        token.

RFC 6749 - The OAuth 2.0 Authorization Framework

この認証フローでは、トークン発行時のリクエストに、username, password を指定する。そのusername, password の組み合わせが有効だった場合、有効なリクエストだと判断しアクセストークンを発行する。

実際のフロー

  • モバイルアプリのログインフォームにユーザがusername(ID), passwordを入力する。このusernameとpasswordはアプリケーションのユーザログイン時に使うものと同じ。
  • それをもとにモバイルアプリがAPIトークン発行リクエストをAPIへ送信する。
  • APIでusername, password の組み合わせをDBと照合し、正しければアクセストークンを発行する。
  • 以後そのアクセストークンでモバイルアプリはAPIリクエストを送信する。

ユーザによるリソースへのアクセス制限

モバイルアプリからリクエストパラメータを改ざんして送信されることは起こり得る。 例えば、ユーザ情報変更APIをモバイルアプリから利用するとする。ユーザIDによるリソースのアクセス制限を設定してない場合のリクエストパラメータを他人のIDに書き換えてリクエストを送信されると他人のリソースが変更できてしまうという重大な脆弱性になる。

この問題を回避するために、

  • アクセストークン発行時に ユーザIDを発行したアクセストークンと紐付けて保存する
  • 個人情報を扱うAPIリソースへのアクセス時には、そのアクセストークンを発行したユーザIDとAPIで参照しようとしているユーザIDが一致しているかチェックする。一致している場合はアクセスを許可、一致していない場合はアクセスを拒否しエラーを返す。

まとめ

この認証フローはモバイルアプリケーションがユーザのアプリケーションへのログインID, passwordを知る必要がある。つまりログイン情報を知っても問題ないアプリケーションから利用する場合に限られる。なので自社製アプリなど信頼できるアプリケーションにこの認証フローは使えない。

認証フローに限らずAPI設計を考える上で、Web API: The Good Parts にはかなりお世話になった。良書。

Web API: The Good Parts

Web API: The Good Parts

利用したライブラリとか

OAuth2 サーバライブラリ(PHP)を利用

GitHub - bshaffer/oauth2-server-php: A library for implementing an OAuth2 Server in php