EN

NRIセキュア ブログ

FAPIとは?|高度なセキュリティレベルのAPIガイドラインを徹底解説

目次

    blogtop

    昨今、企業が自社のサービスのAPIを公開し、他システムと連携することで、自社サービスの価値向上や新たなサービスを創出する動きが盛んです。しかし、APIを公開する際に課題となるのが「セキュリティの担保」です。個人情報の漏洩や攻撃者による不正操作が発生してしまった場合、企業の信用は落ちかねません。

     

    本記事では、金融業界などの特に高いAPIセキュリティレベルを要求されるシステムへのガイドラインであるFAPI(Financial-grade API)について、その要求事項や検討のポイントをご紹介します。

     

    ▶「大規模ユーザを管理する「顧客ID統合プロジェクト」成功の秘訣」を読む

     

    FAPI(Financial-grade API)とは

    Webアプリケーションやモバイルアプリケーションの普及に伴って、ユーザデータへのアクセスが異なるアプリケーション間で共有される機会が増えてきています。それを受けて、ユーザの認証や認可をセキュアに行うためのオープンスタンダードとしてOAuth 2.0 や OpenID Connectといった認証・認可フレームワークは広く利用されるようになりました。

     

    そのOAuth 2.0 や OpenID Connectをベースに、金融業界等のより高いAPIセキュリティレベルを要求されるシステムへのガイドラインとして、OpenID Foundation の FAPIワーキンググループが策定した技術仕様がFAPIです。FAPIは「FAPI 1.0 Final[1][2]」が2021年3月に公開されました。現在FAPIワーキンググループは「FAPI 2.0」の仕様策定の議論に入っています。以下、本記事では「FAPI」と記載した場合には、「FAPI 1.0 Final[1][2]」のことを指します。

    FAPI1.0の仕様概要を解説

    FAPIは、以下の2つの仕様から構成されています。Part1は中程度の固有リスクをもつAPI向けとして、Part2はより高い固有リスクをもつAPI向けとして仕様が定められています。

    • Part1: Baseline (旧称:Read-Only API Security Profile)[1]
    • Part2: Advanced(旧称:Read and Write API Security Profile)[2]

    なお、ドラフト版ではそれぞれPart1が読み取り専用での金融データ利用かそれに類するユースケース、Part2が金融データの読み取りと書き込みおよびリスクの高いユースケースでの利用を想定していると記載されていましたが、FAPI 1.0 Finalにて上記のような表現に修正されました。

    FAPI1.0概要

    FAPI概要

    FAPIではOAuth2.0/OpenID Connectおよびその関連仕様にて定義された仕様の使い方について、クライアントアプリケーションや認可サーバといったシステム毎に要求事項が記載されています。そのため、処理の流れが事前にイメージできていないとなかなか理解が難しい内容になっています。

     

    本記事ではFAPI理解のための取っ掛かりとなることを目的に、FAPIの中でも特徴的な仕様を中心にシーケンス例に沿って紹介しようと思います。

     

    下図はWebアプリケーションがクライアントとなり、ブラウザ(UserAgent)からアクセスした場合のシーケンス例です。

    シーケンス図

    シーケンス図

     

    認可要求

    まずは認可要求についてです。

     

    認可要求・応答はUserAgentを介してやり取りされるため、改竄されやすいポイントです。FAPI Part2では改竄検知の仕組みとしてリクエストオブジェクトを利用することが必須となっています。

     

    リクエストオブジェクトとは、認可要求に必要なパラメータをJSON化して、JWT形式に変換したものです。JWT形式にすることで、どのクライアント「iss」がどの認可サーバ「aud」に対して発行した認可要求なのかを確認することができ、JWTの署名を検証することで各パラメータが改竄されていないことを確認できます。

     

    リクエストオブジェクトのペイロード部の例
    { 
      "client_id": "client_name", 
      "iss": "client_name", 
      "aud": "https://openid.example.com/api/connect/v1", 
      "response_type": "code", 
      "scope": "openid", 
      "redirect_uri": "https://client.example.com/cb", 
      "nonce": "59af138b-dee5-41e6-ae1c-f25b4883d93a", 
      "state": "04d49a49-81c9-4ed6-b924-e04c3d688ea9", 
      "code_challenge_method": "S256", 
      "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", 
      "response_mode": "jwt", 
      "iat": 1695192894, 
      "nbf": 1695192894, 
      "exp": 1695196494 
    } 
    

     

    認可要求時にクライアントから認可サーバにリクエストオブジェクトを伝達する方法は2パターン(後述のPARを含めると3パターン)あり、以下いずれかのパラメータを利用します。

     

    • request:リクエストオブジェクトをクエリパラメータに指定する
    • request_uri:リクエストオブジェクトの格納先URIをクエリパラメータに指定する

     

    request_uriパラメータを利用する場合は、クライアントは事前にリクエストオブジェクトを生成しておき、リクエストオブジェクトを応答するためのURIを用意しておく必要があります。認可サーバはrequest_uriに指定されたURIにリクエストすることでリクエストオブジェクトを取得します。

    request利用時のシーケンス図

    request利用時のシーケンス図

    request_uri利用時のシーケンス図

    04

     

     

    また、Part2ではPushed Authorization Requests(PAR)をサポートしてもよいとされています。認可サーバは新たにPARエンドポイントを用意しておき、クライアントは事前にPARエンドポイントに対して認可要求時のパラメータを登録、その後PARエンドポイントより返却されたrequest_uriを認可要求時のパラメータに指定します。 

    PAR利用時のシーケンス図

    PAR利用時のシーケンス図

    認可応答

    次に認可応答について説明します。認可応答についても認可要求時と同様に改竄への対策がPart2にて記載されており、それが以下の2つの仕様です。

     

    • ID Token as detached signature
    • JWT Secured Authorization Response Mode (JARM)

     

    ID Token as detached signatureは認可応答時にIDトークンを返却し、そのIDトークンをもって応答内容の改竄を検知する仕組みです。認可要求時のresponse_typeパラメータにてcode id_tokenを指定された場合、認可サーバは認可応答にてIDトークンを応答しますが、このIDトークンにcodeやstateパラメータのハッシュ値をそれぞれc_hash、s_hashとして含めます。

     

    クライアントは認可応答にて得られたcodeやstateのハッシュ値を算出し、IDトークン内のc_hash、s_hashの値と一致していること、IDトークンの署名が正しいことを検証することで、認可応答が改竄されていないことを確認できます。

    認可応答(ID Token as detached signatureを利用)の例
    HTTP/2 302
    location:
    https://client.example.com/cb?state=04d49a49-81c9-4ed6-b924-e04c3d688ea9&code=E-w7Dwg-Ag9-l5_mVFTJyCyinEHwWclNGxf3d9ZOlXk&id_token=eyJhbGciOiJQUzI1NiJ9.eyJpc3MiOiJodHRwczovL29wZW5pZC5leGFtcGxlLmNvbS9hcGkvY29ubmVjdC92MSIsInN1YiI6IjVRbTZYa0VoU0lKNFkwaHpZalRHSUhwWVVyZkNuYlBNN0hrayIsImF1ZCI6WyJjbGllbnRfbmFtZSJdLCJleHAiOjE2OTY5OTQ5NzcsImlhdCI6MTY5NjkwODU3Nywibm9uY2UiOiI1OWFmMTM4Yi1kZWU1LTQxZTYtYWUxYy1mMjViNDg4M2Q5M2EiLCJjX2hhc2giOiJMVno2aVA0YWZNOFpiM1MwS21QdU1BIiwic19oYXNoIjoiQlpvQUdTV1MxVVJMd01xdGNnUDVpdyJ9.Waf4JqKRocC6CBgKOFSqzV8suvbYJ40VFoU1j1ymRrkXzs2e6u-n7HUhQjZqUgtK-X0cDVe4kakGBbn2ghXcFfCov3tLSdGsDmtladop_CYDW3P1Q4tHZxyZlwUtbFifGPibtMkffJWSJ_7rvd9d2qadgBucawE-0nqIxW1Mskxbw3Z68d3qnkLmKo0Gz67rn2D5aNfk2R_ZAgAQZnyqGGdxpeZOXc9PiZvM-Bedn4M27A94hHLYrVhw9jingXY9JrcuVx_w0gZSbFBjOj3djyjjt1hdb23c21DUE_I4cOuGJvFVjhbG9OXxefRSiYZpRIkoRR_Ui5t09SN4Sa9aHQ

     

     IDトークンのペイロード部の例

    {
      "iss": "https://openid.example.com/api/connect/v1", 
      "sub": "5Qm6XkEhSIJ4Y0hzYjTGIHpYUrfCnbPM7Hkk", 
      "aud": [ 
        "client_name" 
      ], 
      "exp": 1696994977, 
      "iat": 1696908577, 
      "nonce": "59af138b-dee5-41e6-ae1c-f25b4883d93a", 
      "c_hash": "h8LsiB70ycnrrkW0Gxy4qw", 
      "s_hash": "lzsqKob_tKBBjLR3tttx8Q" 
    } 
    

     

    改竄検知の仕組みとしてID Token as detached signatureは認可応答にIDトークンを含めることで実現したのに対し、JARMは認可応答自体をJWT化することで改竄検知を可能としています。認可要求時にresponse_modeパラメータにてjwtを指定することで認可サーバは認可応答にてresponseというJWT形式のパラメータを返却します。クライアントは応答されたJWTの署名を検証することで改竄を検知することが可能です。

    認可応答(JARMを利用)の例
    HTTP/2 302
    location:
    https://client.example.com/cb?response=eyJraWQiOiJrMiIsImFsZyI6IlBTMjU2In0.ewogICJhdWQiOiAiY2xpZW50X25hbWUiLAogICJjb2RlIjogInNqekRjbEhxSWhYOTk5cjRheTN6OFgwLVpVTGpCQzBfVy15OVktVkpaYjQiLAogICJpc3MiOiAiaHR0cHM6Ly9vcGVuaWQuZXhhbXBsZS5jb20vYXBpL2Nvbm5lY3QvdjEiLAogICJzdGF0ZSI6ICIwNGQ0OWE0OS04MWM5LTRlZDYtYjkyNC1lMDRjM2Q2ODhlYTkiLAogICJleHAiOiAxNjk1MTk2NDk0Cn0.GMndrb4AJync2ZuMlQr6yp9KVPXkp0IhkuKCW9M1ZB2hdUYRMVSUoL1qT5w2bKg8tC3T2sgQm9dIGi3oX5NmNiRAENsTG9DVCzxNSzVobs8swOW_EJ7Zg5V33mf692d7o1y3mFnyu7g0cUgkEsnNFK7l_Bq8UKOhN0jT2NW5wTC22ru_QrAVWBbVzQUdTKYPCD2_u-uPy5LLLMa9vUjBTZ1vm4hk284OSc1l2d9sj9cRaTHEYBVtroXYAu1mn3iRSWDuvdhmpGHKjLdEpyXmlA3QfCf_lanexGOA1eEGUvos9uMW7TUdT8bSaMrQIF8fsowMRa9SY0KfU8otHdB7Lw
    
    responseパラメータのペイロード部の例
    {
      "aud": "client_name",
      "code": "sjzDclHqIhX999r4ay3z8X0-ZULjBC0_W-y9Y-VJZb4",
      "iss": "https://openid.example.com/api/connect/v1",
      "state": "04d49a49-81c9-4ed6-b924-e04c3d688ea9",
      "exp": 1695196494
    }
    

    トークン要求

    トークンエンドポイントの話をする前にまずはFAPIで利用可能なクライアントタイプ・クライアント認証方式について確認します。

     

    FAPI1.0ではPart1, 2で利用できるクライアントタイプ・クライアント認証方式が異なります。具体的には以下の通りです。

    クライアントタイプ・認証方式クライアントタイプ・認証方式

     

    Part2ではPublicクライアントのサポートが禁止されています。また、client_secret_basicやclient_secret_postなどクライアントシークレットを直接指定するクライアント認証方式はFAPI 1.0 Part1, 2ともに利用できません。

     

    トークン要求時に行われるクライアント認証方式についてFAPIの特徴的な仕様として挙げられるのが、tls_client_authやself_signed_tls_client_authのようなクライアント証明書を用いた認証方式(MTLS)ではないでしょうか。

     

    MTLSと聞くと、TLS接続時にクライアント証明書を要求してクライアント認証を行う仕様をイメージする方も多いかと思います。ただ、OAuth2.0におけるMTLSでは上記に加えて認可サーバが発行したアクセストークンをクライアント証明書と紐づけて管理することでアクセストークンの保有者であるクライアントの正当性を確認する目的があります。

     

    クライアント証明書と紐づけられたアクセストークンはsender-constrained access tokensと呼ばれ、クライアントはそのアクセストークンを使ってリソース要求を行う際にはトークン要求時に提示したものと同じクライアント証明書をリソースサーバにも提示する必要があります。

     

    リソースサーバはクライアント証明書のサムプリント値(X.509 Certificate SHA-256 Thumbprint)を算出し、アクセストークンの形式によって以下のいずれかの方法でクライアント証明書とアクセストークンの紐づきが正しいことを確認します。

     

    • アクセストークンがJWT型の場合:cnfクレーム内のx5t#S256の値を参照
    • アクセストークンがJWT型でない場合:Token Introspection応答のx5t#S256の値を参照

    ※Token Introspectionとは、リソースサーバが、クライアントから受け取ったアクセストークンの正当性を、認可サーバに問い合わせるためのエンドポイント

    JWT型アクセストークンのペイロード部の例
    {
      "iss": "https://openid.example.com/api/connect/v1",
      "sub": "57mFD1xBj88mO5TBa1MvfiWoNxz7XzvgxRmh",
      "exp": 1695196380,
       "nbf": 1695195780,
       "cnf": {
        "x5t#S256": "oFFNBi0quU0tHzwkMrZcVUiNvH_tatvUFnQGBjDlPQU"
      }
    }
    

    Token Introspection応答の例
    HTTP/2 200
    content-type: application/json;charset=utf-8
     
    {
      "sub": "57mFD1xBj88mO5TBa1MvfiWoNxz7XzvgxRmh",
      "scope": "openid",
      "iss": "https://openid.example.com/api/connect/v1",
      "active": true,
      "cnf": {
        "x5t#S256": "oFFNBi0quU0tHzwkMrZcVUiNvH_tatvUFnQGBjDlPQU"
      },
      "token_type": "Bearer",
      "exp": 1695196380,
      "client_id": "client_name"
    }
    

    FAPI対応の認証認可基盤を構築するうえでの検討ポイント

    本章では、実際にFAPI対応の認証認可基盤を構築していく中で得られた所感について述べようと思います。

    ポイント① 認可要求:requestまたはrequest_uriの利用

    2.1章にてFAPI1.0 Part2ではリクエストオブジェクトを利用する必要があること、リクエストオブジェクトを伝達する方式が2パターン(PARを含めると3パターン)あることをお伝えしました。ここで悩むのが実際にサービスを提供するならクライアントはどの方法を選ぶべきかです。簡単に各パターンの特徴を下記表にまとめました。

    認可要求パターンの比較表認可要求パターンの比較表

    どのパターンにおいても認可要求パラメータの完全性という観点では特に違いはありません。リクエストオブジェクトを利用することでJWTの署名検証により完全性を担保することになります。

     

    クライアント側実装の容易さの観点ではrequestパラメータを利用するパターン①のシーケンスが一番シンプルであり、反対にrequest_uriパラメータを利用するパターン②はクライアント側でリクエストオブジェクトを応答するためのエンドポイントの作りこみが必要となるため、実装の難易度は高くなると思われます。

     

    認可パラメータの機密性の観点ではパターン②、③はクライアント/認可サーバ間の直接通信にてリクエストオブジェクトのやり取りがなされるため、UserAgentからは認可要求パラメータの中身を参照することができません。

     

    一方でパターン①では一般的な認可コードフローと同様、認可要求パラメータであるリクエストオブジェクトをUserAgentを介して認可サーバに伝達するため、UserAgentにて閲覧が可能です。UserAgentから認可要求パラメータを参照できないようにするためにはリクエストオブジェクトの暗号化が必要となります。

     

    先ほどクライアント側実装の容易さの観点ではパターン①のシーケンスが一番シンプルであると説明しましたが、リクエストオブジェクトの暗号化の処理を実装するとなるとクライアント側の処理は複雑になってしまいます。

     

    以上の観点から、個人的には認可パラメータの機密性が要求されるサービスであるかどうかが一つの決め手になるのではないかと考えます。機密性が要求されないサービスであればパターン①を利用し、機密性が要求されるサービスであればパターン③を利用するという具合に検討してみるのはいかがでしょうか。

    ポイント② 認可応答:ID Token as detached signature または JARMの利用

    次に、認可応答での検討ポイントであるID Token as detached signatureまたはJARMの利用について考えます。こちらも2.2章でご説明したようにFAPI1.0 Part2に対応する場合にはどちらかの利用が必須です。

     

    一見するとID Token as detached signatureを利用する方が従来のベーシックな認可応答(Hybrid Flow)と変わらないので、こちらの方が実装も容易だと思われる方もいるかもしれません。確かにFAPIではc_hashやs_hashの突合に関する追加条件はありますが、この処理自体は比較的簡単に実装できるのではないかと思います。

     

    なぜならばIDトークンを利用するうえで元々iss(発行者)やexp(有効期限)などクレーム内の値を検証する仕組みは備えておく必要があり、そこに新たにc_hashやs_hashの検証を追加する程度で済むはずだからです。

     

    しかしながらID Token as detached signature を利用するにあたって注意すべきとても重要な説明がFAPI Part2では下記のように記載されています。

    In addition, if the response_type value code id_token is used, the authorization server(省略)6.should not return sensitive PII in the ID Token in the authorization response, but if it needs to, then it should encrypt the ID Token.

    出所:FAPI 1.0 Fainal Part2 [2]

     

    上記の説明に登場するPIIとはPersonally Identifiable Informationの略であり、個人を特定可能な情報を意味します。わかりやすい例だと氏名、住所、生年月日、電話番号などがPIIに該当します。また、その情報単体では個人を特定できなくても複数の情報と組み合わせることで個人を特定できるような情報の場合にもPIIに該当します。

     

    ID Token as detached signatureを利用する場合には認可応答で返却されるIDトークンがクエリパラメータとしてUserAgentに渡されるため、そのIDトークンは外部に流出する前提で仕様を検討しなければいけません。少しでもPIIに該当する可能性がある情報が含まれる場合にはIDトークンの暗号化が必須となります。

     

    そうなると当然クライアント側はIDトークンを復号化する処理を実装することが要求されることになり、クライアント側の実装はもうひと手間かかります。加えて運用面でも定期的な鍵情報の変更の考慮もより一層必要になるでしょう。

     

    その一方でJARMは認可応答がJWT形式で返却されるとはいえ、基本的には認可応答にPIIは含まれないため上記のようなPIIへの考慮は不要です。

    ※クライアントが認可要求時のstateにPIIを含めてしまうなど特殊なケースを除きます。

     

    クライアント側に特別な事情や制約がない限りはJARMを利用する方がサービスを展開するうえでは使いやすい仕様なのではとないかと考えます。

    ポイント③ トークン要求:クライアント認証方式の検討

    最後にトークン要求時に求められるクライアント認証方式に関する検討ポイントについても触れようと思います。2.3章でご説明した通り、Part1,2で利用可能なクライアント認証方式は異なりますが、大まかに分けるとMTLSをクライアント認証に利用する方式(tls_client_auth/self_signed_tls_client_auth)とJWTをクライアント認証に利用する方式(client_secret_jwt/ private_key_jwt)に区別できます。

     

    クライアント認証方式を選択するにあたって大きく関係してくる点がsender-constrained access tokensの利用有無です。Part1,2ではそれぞれ以下のような記載があります。

    Part1 5.2.2.The authorization server(省略)should issue access tokens with a lifetime of under 10 minutes unless the tokens are sender-constrained; and

    出所:FAPI 1.0 Fainal Part1 [1]

    Part2 5.2.2.In addition, the authorization server(省略)shall only issue sender-constrained access tokens;

    出所:FAPI 1.0 Fainal Part2 [2]

     

    つまり、Part1においてはsender-constrained access tokensでないアクセストークンの有効期間は10分以内でなければならず、Part2においてはsender-constrained access tokens以外のアクセストークンを利用してはいけないということです。

     

    sender-constrained access tokensとは少なくともFAPI1.0の文脈においてはクライアント証明書と紐づけられたアクセストークンのみを意味しますので、結果的にFAPI1.0に対応するためにクライアント証明書が必要になるケースは多いのではないでしょうか。

     

    Part2でもprivate_key_jwtの利用を認められてはいますが、アクセストークンを発行する際にクライアント証明書が必須になる以上あえてクライアント認証方式をprivate_key_jwtとする利点は少ないのではと思います。

     

    tls_client_auth とself_signed_tls_client_authに関しては個人的にはどちらを利用してもそこまで大した差はないように思います。強いていうならクライアント証明書発行のしやすさという点でself_signed_tls_client_authの方が自己署名証明書でよいことからクライアント証明書発行は容易なケースが多いかもしれません。実際には各サービス次第ですので利用しやすい方を選択することになりそうです。

     

    ただ、tls_client_auth 、self_signed_tls_client_authのどちらを利用するケースにおいても注意しておきたいのは複数のクライアント間で同じクライアント証明書(または同じDNを持つ証明書)を使いまわししてはいけないということです。これは万が一いずれかのクライアントでクライアント証明書が漏洩した場合に、そのクライアント証明書を利用するすべてのクライアントが危険にさらされることになるためです。

     

    当たり前のことではありますが、同じ組織内に複数のクライアントを有する場合にこのようなことは起こりがちです。こちらはPart2の8.10.でも言及されています。クライアント証明書の発行およびその運用には十分に気を付けましょう。

    まとめ

    本記事ではFAPIの概要と対応する際に検討が必要なポイントに注目してご説明しましたが、FAPIでは他にも守らなければならない細かい仕様がたくさんあります。実際にFAPIに準拠したサービスを展開する際には必ず原文の確認をするようにしてください。また、FAPIの仕様だけでなく、OAuth2.0/OpenID Connectのコア仕様や関連仕様も含めて理解する必要がある点も留意してください。

     

    FAPIが少しでも多く世間に浸透し、より安心安全なID連携サービスが提供されることで金融機関を含む様々なサービスが展開され、ユーザエクスペリエンスの向上につながることを願います。

     

    NRIセキュアには、FAPIを含めたOAuth2.0/OpenID Connect等デジタルアイデンティティの標準仕様の専門的な知識を持つメンバが多く在籍しております。また、当社のコンシューマ向けID管理製品「Uni-ID Libra」もFAPI1.0 Part1/Part2に対応しております。

     

    FAPI対応の検討段階から、実際の構築・導入まで一気通貫でご支援可能です。お気軽にお問い合わせください。

     

     

    [1]Financial-grade API Security Profile 1.0 - Part 1: Baseline https://openid.net/specs/openid-financial-api-part-1-1_0.html

    [2]Financial-grade API Security Profile 1.0 - Part 2: Advanced https://openid.net/specs/openid-financial-api-part-2-1_0.html

    BtoCサービスに特化した 顧客ID統合・管理ソリューション Uni-ID Libra  製品情報はこちら