金融グレード Amazon API Gateway

はじめに

このチュートリアルでは、『証明書に紐付いたアクセストークン』を活用して Amazon API Gateway 上に構築した API をこれまで以上に安全に保護する方法を紹介します。

OAuth アクセストークンが一度漏洩すると、攻撃者はそのアクセストークンをもって API にアクセスできます。従来のアクセストークンは電車の切符のようなもので、一度盗まれたら誰でも使えてしまいます。

この脆弱性はアクセストークンと同時にアクセストークンの正当な保有者である証拠も併せて提示することを API 呼出者に要求することで軽減することができます。その証拠は proof of possession と呼ばれ、よく PoP と短縮されます。使用時に PoP を必要とするアクセストークンは、搭乗時にパスポートの提示も併せて要求される国際線の航空券に似ています。

RFC 8705 (OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens) は PoP の仕組みを標準化しました。OAuth コミュニティーではその仕組みは MTLS と呼ばれていますが、混乱を避けるため個人的には『証明書バインディング』と呼んでいます。いずれにしても、簡潔に述べますと、その仕組みは、トークンエンドポイントからアクセストークンの発行を受ける際に使用した **X.509 証明書**と同じ証明書をアクセストークンと併せて提示することを API 呼出者に要求します。次の図は証明書バインディングの概念を示しています。


証明書バインディング

金融グレード API (英語名称 Financial-grade API)、通称 FAPI (ファピ) をご存知でしょうか。FAPI は、より高度な API セキュリティのために OAuth 2.0OpenID Connect の上に作られた技術仕様です。英国オープンバンキングはオープンバンキングプロファイルの基盤として FAPI を採用し、今では他国も追随しつつあります。ここで注目していただきたいのは、証明書バインディングが FAPI の必須技術要素となっている点です。

証明書バイディングの大前提は、API とクライアントアプリケーション間の接続が相互 TLS (Mutual TLS) を用いて確立されることです。相互 TLS では、TLS ハンドシェイク中にクライアントアプリケーション側も X.509 クライアント証明書の提示を要求されます。2020 年 9 月 17 日、AWS は "Introducing mutual TLS authentication for Amazon API Gateway" と題したブログで Amazon API Gateway が相互 TLS に対応したことを発表しました。Amazon API Gateway は金融グレード API セキュリティへの扉を開けました。

オーソライザーの役割

Amazon API Gateway は、API 保護の独自ロジックを開発者自ら実装する手段として **Lambda オーソライザー**という仕組みを提供しています。OAuth ベースの API 保護を提供する Lambda オーソライザーは、API コールからアクセストークンとクライアント証明書を取り出し、次の事項を確認します。

  1. 提示されたアクセストークンが存在し、
  2. 有効期限が切れておらず、
  3. リソースアクセスに要求されるスコープを持ち、
  4. 無効化されておらず、
  5. 提示されたクライアント証明書に紐付いている。 (証明書バインディング)

その後、オーソライザーは確認結果に応じて次のいずれかの処理をおこないます。

  • リソースアクセスを許可する IAM ポリシーを返す。
  • リソースアクセスを拒否する IAM ポリシーを返す。
  • HTTP ステータスコード “401 Unauthorized” を用いてリソースアクセスを拒否するように Amazon API Gateway に指示するため、エラーメッセージ 'Unauthorized' を持つ例外を投げる。
  • HTTP ステータスコード “500 Internal Server Error” を用いてリソースアクセスを拒否するように Amazon API Gateway に指示するため、他のエラーメッセージを持つ例外を投げる。

オーソライザーの実装

この後すぐにお見せすることになる Lambda オーソライザーの実装では、アクセストークンの検証処理を Authlete (オースリート) のイントロスペクション API (/api/auth/introspection) に委譲します。そのため、そのオーソライザーの実装は複雑なロジックを含みません。次の図は Amazon API Gateway、Lambda オーソライザー、Authlete の関係を示しています。


Amazon API Gateway、Lambda オーソライザー、Authlete の関係

これに加え、Authlete の Python ライブラリ内にある Authorizer クラスが必要な作業のほとんどを行うため、実装は非常に小さくできます。実際に、次のコードは証明書バインディングをサポートする Lambda オーソライザーの完全な実装例です。

from authlete.aws.apigateway.authorizer import Authorizer

authorizer = Authorizer()

def lambda_handler(event, context):
    return authorizer.handle(event, context)

リソースアクセスに要求されるスコープ

しかしながら、実際には、どのリソースがどのスコープを要求するかを設定する必要が出てくるでしょう。これは、(1)Authorizerhandle() メソッドに関数を渡す、もしくは(2)Authorizer のサブクラスを作り determine_scopes() メソッドをオーバーライドする、という方法で実現することができます。

関数とメソッドの双方とも、下表に挙げられている 4 つの引数を取り、リソースアクセスに必要となるスコープの名前のリストを返すことが求められます。

引数 説明
event オーソライザーに渡されたイベント
context オーソライザーに渡されたコンテキスト
method リソースアクセスの HTTP メソッド
path リソースのパス

次の 2 つの例はどちらも同じ効果を持ち、time リソースに HTTP GET メソッドでアクセスする際には time:query スコープが必要であると言っています。

from authlete.aws.apigateway.authorizer import Authorizer

authorizer = Authorizer()

def determine_scopes(event, context, method, path):
    if method == 'GET' and path == 'time':
        return ['time:query']

    return None

def lambda_handler(event, context):
    return authorizer.handle(event, context, determine_scopes)
from authlete.aws.apigateway.authorizer import Authorizer

class CustomAuthorizer(Authorizer):
    def determine_scopes(self, event, context, method, path):
        if method == 'GET' and path == 'time':
            return ['time:query']

        return None

authorizer = CustomAuthorizer()

def lambda_handler(event, context):
    return authorizer.handle(event, context)

ポリシーまたは例外

OAuth 2.0 関連の仕様に準拠するためには、場合によっては (例えば提示されたアクセストークンの有効期限が切れているとき) “401 Unauthorized” を API 呼出者に返すよう、Lambda オーソライザーから Amazon API Gateway に伝えなければなりません。AWS の技術文書やサンプルプログラムによれば、これを行うためにオーソライザーは 'Unauthorized' というメッセージを持つ例外を投げなければなりません。しかし、このような簡素な例外は “Unauthorized” レスポンスに関する全ての貴重な情報を捨ててしまい、デバッグ作業をとても難しくしてしまいます。

そこで、デフォルトでは、別の言い方をすると policy プロパティーが True (デフォルト) の場合、Authorizerhandle() メソッドは、'Unauthorized' 例外を投げるべきである場合を含め、常に許可 (“Allow”) もしくは拒否 (“Deny”) を示す IAM ポリシーを返します。

もしも ”Unauthorized" や “Internal Server Error” の際に Authorizer に例外を投げさせたければ、policy プロパティーに False を設定してください。Authorizer のコンストラクターに policy=False を渡すことで実現できます。

authorizer = Authorizer(policy=False)

ポリシーのコンテキスト

Authorizerhandle() メソッドは IAM ポリシーを表す dict インスタンスを返します。その辞書内には context があり、その値も辞書となっています。Authorizer はそこに幾つかの情報を埋め込みます。下表は、context 辞書に含まれる可能性のあるプロパティーの一覧です。

プロパティー 説明
introspection_request Authlete のイントロスペクション API へのリクエストを表す JSON 文字列
introspection_response Authlete のイントロスペクション API からのレスポンスを表す JSON 文字列
introspection_exception Authlete のイントロスペクション API コール時に発生した例外を表す文字列
scope 提示されたアクセストークンがカバーするスコープ群をスペース区切りで列挙した文字列
client_id アクセストークンの発行対象のクライアントアプリケーションのクライアント ID
sub クライアントアプリケーションへのアクセストークンの発行を許可したリソースオーナーのサブジェクト (主体識別子) を表す文字列
exp Unix エポック (1970 年 1 月 1 日) からの経過秒数で表現した、アクセストークンの有効期限終了日時
challenge エラー時に WWW-Authenticate HTTP ヘッダーの値として用いるべき値
action Authlete のイントロスペクション API のレスポンスに含まれる action の値
resultMessage Authlete のイントロスペクション API のレスポンスに含まれる resultMessage の値

Authorizer のサブクラスで update_policy_context() をオーバーライドすることにより、context にエントリーを追加することができます。ただし、値として JSON オブジェクトや配列は使えないので注意してください。これは AWS の技術的制限事項です。

class CustomAuthorizer(Authorizer):
    def update_policy_context(self, event, context, request, response, exception, ctx):
        ctx.update({
            'my_key': 'my_value'
        })

Lambda オーソライザーが返すポリシーの context に含まれるエントリー群は、後ほど他の場所で使うことができます。詳細は『Amazon API Gateway Lambda オーソライザーからの出力』を参照してください。

フック

Authorizer クラスはサブクラスの実装のために幾つかのフックを提供します。必要に応じてオーバーライドしてください。

メソッド 説明
determine_scopes() リソースアクセスに要求されるスコープ群を決定する
update_policy_context() ポリシーに埋め込む context を更新する
on_enter() handle() メソッド開始時に呼ばれる
on_introspection_error() Authlete イントロスペクション API 失敗時に呼ばれる
on_introspection() Authlete イントロスペクション API 成功時に呼ばれる
on_allow() 許可 (Allow) を表すポリシー生成時に呼ばれる
on_deny() 拒否 (Deny) を表すポリシー生成時に呼ばれる
on_unauthorized() “Unauthorized” 用に例外が投げられる際に呼ばれる
on_internal_server_error() “Internal Server Error” 用に例外が投げられる際に呼ばれる

オーソライザーの設定

Lambda イベントペイロード

Lambda オーソライザーを作成するにあたり最も重要な点は、Lambda イベントペイロードで『リクエスト』を選ぶことです。さもないと、オーソライザーはクライアント証明書の情報にアクセスできません。それはアクセストークンがクライアント証明書と紐付いているかどうかをオーソライザーがチェックできなくなることを意味します。


オーソライザーの作成

Lambda イベントペイロードの選択によりオーソライザーへの入力がどのように変化するかの詳細については『Amazon API Gateway Lambda オーソライザーへの入力』を参照してください。

環境変数

AuthleteApi のインスタンスが渡されない場合、Authorizer のコンストラクターは内部で AuthleteApiImpl(AuthleteEnvConfiguration()) を実行してインスタンスを生成し、それを Authlete API へのアクセス時に使用します。そこで使用されている AuthleteEnvConfiguration が Authlete の設定情報を環境変数経由で取得できることを想定しているので、オーソライザーの実装として用いる Lambda 関数に次の環境変数群を設定しておく必要があります。

環境変数 説明
AUTHLETE_BASE_URL Authlete サーバーのベース URL
AUTHLETE_SERVICE_APIKEY サービスに割り当てられた API キー
AUTHLETE_SERVICE_APISECRET サービスに割り当てられた API シークレット


Lambda 関数の環境変数

タイムアウト

Lambda 関数のタイムアウトをデフォルト値よりも大きくしておくことをお勧めします。様々な条件が重なることにより、Authlete のイントロスペクション API の応答に時間がかかることがありうるためです。


Lambda 関数のタイムアウト

オーソライザーのパッケージング

Lambda 関数の ZIP パッケージの生成とアップロードの方法は『Python の AWS Lambda デプロイパッケージ』の『追加の依存関係を使用して関数を更新する』で説明されています。

下記は、authlete パッケージを含む Lambda オーソライザーの ZIP ファイルを生成してアップロードする例です。

~$ mkdir authorizer
~$ cd authorizer
~/authorizer$ vi lambda_function.py
~/authorizer$ pip install --target ./package authlete
~/authorizer$ (cd package; zip -r9 ../function.zip .)
~/authorizer$ zip -g function.zip lambda_function.py
~/authorizer$ aws lambda update-function-code --function-name authorizer --zip-file fileb://function.zip

テスト

このチュートリアルにおいて、テストが一番大変な箇所かもしれません。というのは、下図に示すテスト環境を構築するために多くの手順が必要だからです。


証明書バインディングをテストするため構成要素

次の手順を一つずつ一緒に踏んでいきましょう。

  1. 証明書バインディングをサポートする認可サーバーを用意する。
  2. 認可サーバー用のサーバー証明書を用意する。
  3. 認可サーバーのトークンエンドポイントの相互 TLS のためにリバースプロキシを設定する。
  4. 証明書バインディング用に設定されたクライアントアプリケーションを用意する。
  5. クライアントアプリケーション用のクライアント証明書を用意する。
  6. Amazon API Gateway 用のカスタムドメインを用意する。
  7. 証明書に紐付くアクセストークンを取得する。
  8. リソースアクセス (API コール) をおこなう。

認可サーバー

認可サーバーとして java-oauth-server を使うことにしましょう。これは、Authlete をバックエンドサービスとして用いる Java で書かれた認可サーバーです。

$ git clone https://github.com/authlete/java-oauth-server
$ cd java-oauth-server
$ vi authlete.properties
$ docker-compose up

上記のコマンド群を実行すると、ローカルマシン上の http://localhost:8080 で認可サーバー (java-oauth-server) が起動します。

それから、管理者コンソールにログインし、認可サーバーに対応するサービスの設定を証明書バインディングをサポートするように変更します。


証明書バインディング用のサービス設定

サーバー証明書

認可サーバー用の秘密鍵および自己署名証明書を作成します。

$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256    > server_private_key.pem
$ openssl req -x509 -key server_private_key.pem -subj /CN=localhost > server_certificate.pem

このチュートリアルで使用している openssl コマンドは OpenSSL のものであることに注意してください。High Sierra 以降、macOS にインストールされている openssl は LibraSSL のものなので、このチュートリアルのコマンドラインをそのまま試すためには OpenSSL の openssl をインストールする必要があります。

$ /usr/bin/openssl version -a
LibreSSL 2.6.5
......
$ brew install openssl
$ /usr/local/opt/openssl/bin/openssl version -a
OpenSSL 1.1.1g  21 Apr 2020
......

リバースプロキシ

java-oauth-server 自身は自分のエンドポイント群を TLS で保護していないので、TLS 接続を受けるためには java-oauth-server の前にリバースプロキシを置く必要があります。次のものは Nginx をリバースプロキシとして動かすための設定ファイルの例です。

events {}
http {
  server {
    # TSL 接続をポート番号 8443 で受け付けます。
    listen 8443 ssl;

    # PEM フォーマットのサーバー証明書
    ssl_certificate /path/to/server_certificate.pem;

    # PEM フォーマットのサーバー秘密鍵
    ssl_certificate_key /path/to/server_private_key.pem;

    # 相互 TLS を有効にします。optional_no_ca はクライアント証明書を要求するものの、
    # それが信頼済み CA 証明書で署名されていることを要求はしません。このチュートリアルの
    # 用途としてはこれで十分です。詳細については ngx_http_ssl_module のドキュメントを
    # 参照してください: http://nginx.org/en/docs/http/ngx_http_ssl_module.html
    ssl_verify_client optional_no_ca;

    # 相互 TLS 接続のクライアント証明書を、背後にあるサーバー (java-oauth-server) に
    # 'X-Ssl-Cert' HTTP ヘッダーの値として渡します。背後にあるサーバーのこの HTTP
    # ヘッダーを認識できる必要があり、java-oauth-server は認識できます。正確には、
    # java-oauth-server が利用している authlete-java-jaxrs ライブラリが認識します。
    proxy_set_header X-Ssl-Cert $ssl_client_escaped_cert;

    # Nginx が 'https://localhost:8443/token' で受けたリクエストを
    # 'http://localhost:8080/api/token' (java-oauth-server の '/api/token') に
    # 転送します。TLS はここで終端されます。
    location = /token {
      proxy_pass http://localhost:8080/api/token;
    }
  }
}

この設定により、Nginx は https://localhost:8443 で起動し、/token で受け付けたリクエストを http://localhost:8080/api/token に転送します。

この設定ファイルの名前が nginx.conf の場合、次のコマンドにより Nginx を起動することができます。

$ nginx -c $PWD/nginx.conf

Nginx を止めるときは次のコマンドを入力してください。

$ nginx -s stop

クライアントアプリケーション

開発者コンソールにログインし、テストで使用する予定のクライアントアプリケーションの証明書バインディングの設定を有効にしてください。


証明書バインディング用のクライアント設定

クライアント証明書

クライアントアプリケーション用の秘密鍵および自己署名証明書を作成します。

$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > client_private_key.pem
$ openssl req -x509 -key client_private_key.pem -subj /CN=client.example.com > client_certificate.pem

後ほどテストで使うので、秘密鍵と証明書をもう一組作成してください。コモンネーム (/CN=) の値には別の値を指定するようにしてください。というのは、次のセクションで説明しますが、Amazon API Gateway のカスタムドメインの設定が同じサブジェクトの証明書を複数含むトラストストアを拒否するからです。

$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 > client_private_key_2.pem
$ openssl req -x509 -key client_private_key_2.pem -subj /CN=client2.example.com > client_certificate_2.pem

カスタムドメイン

本記事執筆時点では、Amazon API Gateway で相互 TLS を有効にするためには API にカスタムドメイン (例 api.example.com) を割り当てなければなりません。

Amazon API Gateway のコンソールには『カスタムドメイン名』というメニューがあります。そこでのカスタムドメイン設定をスムーズにおこなうため、事前に次のものを用意しておいたほうがよいでしょう。

  1. カスタムドメイン用のサーバー証明書
  2. トラストストア (信頼するクライアント証明書群を含むファイル)

カスタムドメイン用のサーバー証明書

Amazon API Gateway のカスタムドメイン用のサーバ証明書は AWS Certificate Manager (ACM) の管理下に置かなければなりません。ACM コンソールでは、既存の証明書をインポートしたり新しく証明書を作成したりすることができます。しかし、相互 TLS が有効になっている場合、インポートした証明書は Amazon API Gateway のカスタムドメイン用としては使えないので、新しく作成してください。

トラストストア

相互 TLS を設定する際、信頼するクライアント証明書群を含むファイルの場所を入力するよう求められます。そのファイルはトラストストアと呼ばれます。Amazon API Gateway の相互 TLS の実装は、TLS ハンドシェイク中に提示されたクライアント証明書がトラストストア内に含まれているかチェックします。もしも含まれていなければ、Amazon API Gateway は Lambda オーソライザーを呼ぶことなく、その接続を拒否します。

トラストファイルは、PEM フォーマットのクライアント証明書群を下記のように列挙するテキストファイルです。

-----BEGIN CERTIFICATE-----
<証明書の内容>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<証明書の内容>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<証明書の内容>
-----END CERTIFICATE-----
...

このチュートリアル用のトラストストアは次のコマンドで作成できます。

$ cat client_certificate.pem   >  truststore.pem
$ cat client_certificate_2.pem >> truststore.pem

Amazon API Gateway が参照できるよう、トラストストアは S3 にアップロードする必要があります。

$ aws s3 cp truststore.pem s3://{あなたのS3バケット}

上記手順は『REST API の相互 TLS 認証の設定』に記載されています。詳細はそちらのドキュメントを参照してください。

カスタムドメイン設定

Amazon API Gateway にカスタムドメインを登録するための準備が整いました。

『ドメインの詳細』で相互 TLS 認証を有効にしてください。そうすると、トラストストア URI を入力するフィールドが表示されます。あなたのトラストストアの S3 URI をそのフィールドに入力してください。


カスタムドメインの詳細

それから、『エンドポイント設定』でカスタムドメイン用のサーバ証明書を選択してください。


カスタムドメインのエンドポイント設定

カスタムドメイン設定後、APIとステージの組をカスタムドメイン下のパスにどのようにマッピングするかを設定することができます。次のスクリーンショットでは、“Example” API の dev ステージをカスタムドメインのパス dev にマッピングしています。


API マッピングを設定

ルーティング

カスタムドメイン設定の最後の手順は、カスタムドメインが『API Gateway ドメイン名』にルーティングされるよう、DNS サーバーの CNAME レコードを追加することです。


API Gateway domain name

証明書に紐付くアクセストークン

全ての準備が済みました。認可コードフローを用いて証明書に紐付くアクセストークンを発行してみましょう。

認可リクエスト

認可コードフローの最初のステップは、認可サーバーの**認可エンドポイントに Web ブラウザ経由で認可リクエスト**を送ることです。このチュートリアルでは、認可エンドポイントは java-oauth-server が提供する http://localhost:8080/api/authorization です。認可リクエストを表す下記の URL の ${クライアントID} をあなたのクライアントアプリケーションの実際のクライアント ID で置き換え、Web ブラウザを使ってその URL にアクセスしてください。

http://localhost:8080/api/authorization?response_type=code&client_id=${クライアントID}&scope=profile+email&state=123

Web ブラウザには認可サーバーが生成した認可ページが表示されます。次のように見えるでしょう。


認可コードフローの認可ページ

ページにはログイン ID とパスワードを入力するフィールドがあります。そこに johnjohn を入力し Authorize ボタンを押してください。Web ブラウザはあなたのクライアントアプリケーションのリダクレクトエンドポイントにリダイレクトされます。

クライアントアプリケーションのリダイレクト URI をデフォルト値から変更していなければ、リダイレクトエンドポイントの URL は https://{Authleteサーバー}//api/mock/redirection/{サービスAPIキー} です。

ブラウザのアドレスバーに表示されているリダイレクトエンドポイントの URL には、次のように code レスポンスパラメーターが含まれています。

https://{Authleteサーバー}/api/mock/redirection/{サービスAPIキー}?state=123&code=RwRq2Lp0bJVMiLPKAFz4qB1hxieBD1X5HKuv8EPkJeM

code レスポンスパラメーターの値は、あなたのクライアントアプリケーションに対して認可サーバーから発行された認可コードです。この認可コードはクライアントアプリケーションがトークンリクエストを投げる際に必要となります。

トークンリクエスト

認可コード取得後、クライアントアプリケーションは認可サーバーの**トークンエンドポイントトークンリクエスト**を投げます。このチュートリアルでは、トークンエンドポイントは Nginx が提供する https://localhost:8443/token です。

トークンリクエストはシェル端末で curl コマンドを用いて投げることができます。下記はトークンリクエストの例です。タイプする前に、${クライアントID}${認可コード} を実際のクライアント ID と認可コードに忘れずに置き換えてください。

$ curl -k --key client_private_key.pem --cert client_certificate.pem https://localhost:8443/token -d grant_type=authorization_code -d client_id=${クライアントID} -d code=${認可コード}
引数 説明
-k サーバー証明書の検証をしない。このチュートリアルでは自己署名サーバー証明書を使っているので、このオプションが必要。
--key client_private_key.pem クライアントの秘密鍵を指定する。
--cert client_certificate.pem クライアントの証明書を指定する。
https://localhost:8443/token トークンエンドポイントの URL。
-d grant_type=authorization_code 認可コードフローであることを示す。
-d client_id=${クライアントID} クライアント ID を指定する。${クライアントID} を実際のクライアント ID で置き換えること。
-d code=${認可コード} 認可コードを指定する。${認可コード} を実際の認可コードで置き換えること。

ここでポイントとなるのは、--key オプションと --cert オプションを用いてクライアントの秘密鍵と証明書を curl コマンドに渡すことです。トークンエンドポイントが相互 TLS を要求するため、すなわち TLS ハンドシェイク中にクライアント証明書を要求するためです。トークンエンドポイントから発行されるアクセストークンは、トークンリクエストで使用されたクライアント証明書に紐付けられます。

トークンリクエストが成功すると、トークンエンドポイントは access_token を含む JSON を返します。

{
  "access_token":  "b5qgqkXpzObRyceBqKeGPDCT9NX9GGXSt_oSYBSj7GQ",
  "refresh_token": "1iSpvpeznTzwdUJzwRbt-abqE4znWn_yhN5PbBKV9zw",
  "scope":         "email profile",
  "token_type":    "Bearer",
  "expires_in":    86400
}

access_token プロパティーの値が発行されたアクセストークンです。クライアントアプリケーションは API コール時にこのアクセストークンを使います。

リソースアクセス (API コール)

ついに、証明書に紐付くアクセストークンで保護される Amazon API Gateway 上のリソース (API) にアクセスする準備が整いました。

最初に、アクセストークンと正しいクライアント証明書でリソースにアクセスしてください。下記の例の ${アクセストークン}${カスタムドメイン}${リソース} は実際の値で置き換えてください。全てが全て正しく設定されていれば、ブロックされることなくリソースの取得に成功するでしょう。

$ curl --key client_private_key.pem --cert client_certificate.pem -H "Authorization: Bearer ${アクセストークン}" https://${カスタムドメイン}/${リソース}

次に、同じアクセストークンと不正なクライアント証明書 (このチュートリアルでは client_certificate_2.pem) でリソースにアクセスしてください。

$ curl --key client_private_key_2.pem --cert client_certificate_2.pem -H "Authorization: Bearer ${アクセストークン}" https://${カスタムドメイン}/${リソース}

次のエラーレスポンスを受け取ることでしょう。

{"Message":"User is not authorized to access this resource with an explicit deny"}

これは Amazon API Gateway がリソースアクセスを拒否したことを示しています。ここで最も重要なのは、「同時に提示したクライアント証明書がアクセストークンに紐付くものとは異なるため、アクセストークンが正当であるにもかかわらずリソースアクセスが拒否された」ということです。これが証明書バインディングです。

おめでとうございます!

あなたはこのチュートリアルを完了し、証明書バインディング (RFC 8705) を活用してこれまで以上に安全に Amazon API Gateway 上の API を保護することができるようになりました!

サポートが必要であれば**お問い合わせ**ください。いつでも歓迎します!