6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FAPI 2.0 HTTP Signingの紹介

Last updated at Posted at 2024-12-01

HTTP Message Signaturesとは?

HTTP リクエストや HTTP レスポンス (以降まとめて『HTTP メッセージ』と呼びます) に署名する方法は数多く提案されてきました。それらは実装され、実際に運用もされました。しかし、いずれもデファクトスタンダードの地位を獲得できず、長い年月が過ぎました。

そのような歴史を経てようやく、一つの方法が IETF の RFC として承認されました。それが RFC 9421: HTTP Message Signatures です。

この仕様に従って署名された HTTP メッセージには SignatureSignature-Input という二つの HTTP フィールドが含まれます。Signature フィールドには署名が、Signature-Input フィールドには署名に関するメタデータが含まれます。

HTTPメッセージ署名を含むHTTPレスポンスの例
HTTP/1.1 200 OK
Date: Tue, 22 Oct 2024 15:59:44 GMT
x-fapi-interaction-id: e5e7d28c-8d85-40fc-ab5b-3f0a32f4e99e
Content-Digest: sha-256=:RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=:
Signature-Input: sig=("@method";req "@target-uri";req "@status" "content-digest");created=1729612785;keyid="tsq5sQwuoADZ3iARLOreaYaIa9mG5TnV11zpRRjuA0k";tag="fapi-2-response"
Signature: sig=:2m81zeYd/jjtJ5wcViutKk1zHW0XU7yOjN7GBVEPbQ3VnL3dPcuRfqIKVBhrqkNIIdJVDuNwO0IKJxfCyaQYdg==:
Content-Type: text/plain
Content-Length: 2
Server: Jetty(9.3.7.v20160115)

{}

HTTP Message Signatures は柔軟な仕様なので、HTTP メッセージのどの部分を署名処理の入力として使用するかを指定することができます。例えば、HTTP リクエストの (a) HTTP メソッド、(b) 完全 URL、(c) Authorization HTTP フィールドの三つを署名処理の入力として使用する、ということを指定できます。ご推察の通り、これらの情報は Signature-Input フィールドに格納されます。

HTTP メッセージ全体を署名処理の入力としてしまうと、リバースプロキシなどの中間サーバが HTTP メッセージに HTTP フィールドを追加した場合 (実際これは頻繁に行われる)、署名処理の入力値が一致しなくなるので署名検証に失敗してしまいます。そのため、HTTP メッセージの全体ではなく特定部分を選択して署名するための仕組みは、むしろ、HTTP 署名の方法を定義する仕様が必ず備えなければならない機能と言えます。

RFC 9421 内の誤りに気付いたので Errata 8102Errata 8103 として報告しました。簡単に言うと、仕様書内で @signature-input となっている箇所は、正しくは @signature-params です。

FAPI 2.0 HTTP Signingとは?

OAuth 2.0 のリソースサーバとやりとりする HTTP メッセージに対して HTTP Message Signatures を適用する方法を定めた仕様が FAPI 2.0 HTTP Signing です。

この仕様の要点は次の通りです。

リソースリクエストに対する署名
  • HTTP メソッド、完全 URL、Authorization フィールドを署名処理の入力とする
  • DPoP フィールドがあれば、それも入力とする
  • リクエストボディがあれば、Content-Digest フィールドも入力とする
  • created パラメータを必須とする
  • tag パラメータを必須とし、その値は fapi-2-request とする
リソースレスポンスに対する署名
  • リクエストのHTTP メソッド、リクエストの完全 URL、レスポンスの HTTP ステータスを署名処理の入力とする
  • レスポンスボディがあれば、レスポンスの Content-Digest フィールドも入力とする
  • created パラメータを必須とする
  • tag パラメータを必須とし、その値は fapi-2-response とする

現在の FAPI 2.0 HTTP Signing のドラフトには、リソースレスポンスに対する署名を行う際、「リクエストに含まれている Signature フィールドと Signature-Input フィールドも署名処理の入力とする」と書いてあります。

しかし、RFC 9421 自体にわざわざ非推奨と明記されている上、リバースプロキシなどの中間サーバが HTTP 署名を追加した場合 (これは RFC 9421 が明示的に言及している HTTP 署名使用例)、Signature フィールドと Signature-Input フィールドの値がリクエスト送信元が認識しているものと異なってしまうので、署名検証に失敗してしまいます。

そのため、『FAPI ISSUE 718: "signature";req and "signature-input";req are not recommended』を作成し、「Signature フィールドと Signature-Input フィールドを署名処理の入力とすべきではない」と提言しました。

FAPI 2.0 HTTP Signingのデモ

OpenID Foundation の Certification Team は、FAPI 2.0 HTTP Signing 用テストの開発を開始する前に、実際に動く FAPI 2.0 HTTP Signing の実装を欲していました (2024-04-09)。なぜなら、テストを開発しても、そのテストを試す対象がなければ、テストが正しく書けているか確認できないからです。

そのような要望を受け、Authlete 社は FAPI 2.0 HTTP Signing の実装を行いました。そして、FAPI 2.0 HTTP Signing のデモ手順を FAPI ワーキンググループ内で共有しました (2024-10-23)。

下記はそのデモ手順です。テスト用アクセストークンの有効期限が切れるまでは (〜 2027 年 7 月中旬頃)、この手順そのままで動くはずです。

鍵とスクリプトの準備
mkdir fapi-http-signing
cd fapi-http-signing
wget -q https://raw.githubusercontent.com/authlete/oid4vci-demo/refs/heads/main/request-signing.jwk
wget -q https://raw.githubusercontent.com/authlete/oid4vci-demo/refs/heads/main/response-signing.jwk
wget -q https://raw.githubusercontent.com/authlete/oid4vci-demo/refs/heads/main/generate-http-message-signature
wget -q https://raw.githubusercontent.com/authlete/oid4vci-demo/refs/heads/main/verify-http-message-signature
chmod +x generate-http-message-signature verify-http-message-signature
シェル変数の準備
ACCESS_TOKEN=nNLEI2zrz63pNmD3xm79-tOS8qZPwy4LmlwEnQR8UEg
TARGET_URI=https://fapidev-rs.authlete.net/api/fapi/accounts
REQUEST_KEYID=ScIdKFzAj93vywpwjrfi3wrj5oHmHbHfmPT5IPTLmVc
RESPONSE_KEYID=tsq5sQwuoADZ3iARLOreaYaIa9mG5TnV11zpRRjuA0k
リソースリクエスト
REQUEST_CREATED=$((`date +%s` - 10))
REQUEST_SIGNATURE=`./generate-http-message-signature \
   --key request-signing.jwk \
   --baseline "\"@method\": GET" \
   --baseline "\"@target-uri\": ${TARGET_URI}" \
   --baseline "\"authorization\": Bearer ${ACCESS_TOKEN}" \
   --created ${REQUEST_CREATED} \
   --keyid ${REQUEST_KEYID} \
   --tag fapi-2-request \
   --print-signature`
REQUEST_SIGNATURE_METADATA=`./generate-http-message-signature \
   --key request-signing.jwk \
   --baseline "\"@method\": GET" \
   --baseline "\"@target-uri\": ${TARGET_URI}" \
   --baseline "\"authorization\": Bearer ${ACCESS_TOKEN}" \
   --created ${REQUEST_CREATED} \
   --keyid ${REQUEST_KEYID} \
   --tag fapi-2-request \
   --print-signature-metadata`
curl https://fapidev-rs.authlete.net/api/fapi/accounts \
   -H "Authorization: Bearer ${ACCESS_TOKEN}" \
   -H "Signature-Input: sig=${REQUEST_SIGNATURE_METADATA}" \
   -H "Signature: sig=:${REQUEST_SIGNATURE}:" \
   -i
リソースレスポンスの例
HTTP/1.1 200 OK
Date: Wed, 23 Oct 2024 06:33:43 GMT
Server: Jetty(9.4.56.v20240826)
x-fapi-interaction-id: 3f7cbbce-b2be-405a-b599-650bf6879bb4
Content-Digest: sha-256=:RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=:
Signature-Input: sig=("@method";req "@target-uri";req "@status" "content-digest");created=1729665223;keyid="tsq5sQwuoADZ3iARLOreaYaIa9mG5TnV11zpRRjuA0k";tag="fapi-2-response"
Signature: sig=:6mPDn/+4qoi7Kl3JKAYF75L4c4Gs4ocHqeo75CwPtVePLdz9xzS8UfTNuwPy6QlCMh3ZWhm74AX/zl1T+aCA5w==:
Content-Type: text/plain
Content-Length: 2

{}
署名検証のためのシェル変数の準備
RESPONSE_SIGNATURE=${リソースレスポンスのSignatureフィールドのsigの値}
RESPONSE_CONTENT_DIGEST=${リソースレスポンスのContent-Digestフィールドの値}
RESPONSE_CREATED=${リソースレスポンスのSignature-Inputフィールドのcreatedの値}
署名検証のためのシェル変数の準備の例
RESPONSE_SIGNATURE=:6mPDn/+4qoi7Kl3JKAYF75L4c4Gs4ocHqeo75CwPtVePLdz9xzS8UfTNuwPy6QlCMh3ZWhm74AX/zl1T+aCA5w==:
RESPONSE_CONTENT_DIGEST=sha-256=:RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=:
RESPONSE_CREATED=1729665223
署名検証スクリプトの実行
./verify-http-message-signature \
   --key response-signing.jwk \
   --signature ${RESPONSE_SIGNATURE} \
   --baseline "\"@method\";req: GET" \
   --baseline "\"@target-uri\";req: ${TARGET_URI}" \
   --baseline "\"@status\": 200" \
   --baseline "\"content-digest\": ${RESPONSE_CONTENT_DIGEST}" \
   --created ${RESPONSE_CREATED} \
   --keyid ${RESPONSE_KEYID} \
   --tag fapi-2-response
署名検証スクリプトの出力例
true

注意事項

  • テスト用アクセストークンは 2024 年 10 月 20 日に 1000 日を有効期間として作成された。

  • FAPI ISSUE 718 で述べた理由により、リソースレスポンスの署名メタデータに "signature";req"signature-input";req は含めていない。

  • verify-http-message-signature スクリプトは createdtag といった署名パラメータがアルファベット順に並んでいることを想定している。リソースサーバの実装が異なる順番を用いる場合、このスクリプトによる署名検証は失敗する。

HTTP Message Signatures用Javaライブラリ

FAPI 2.0 HTTP Signing 実装の副産物として、HTTP Message Signatures のための Java ライブラリをオープンソースで公開しました (authlete/http-message-signatures)。

RFC 9421 の著者本人が書いた Java ライブラリ (bspk/httpsig-java) も存在しますが、書かれた時期が古いせいもあって仕様に準拠していない箇所が多数あるので、新たに書き起こしました。

使用例については、ライブラリのテスト群 や、FAPI 対応リソースエンドポイントのサンプルコード (FapiResourceEndpoint.java) をご参照ください。

おわりに

FAPI 2.0 Message Signing は次の 5 つの要素で構成されます。

  1. Authorization Request Signing - JAR
  2. Authorization Response Signing - JARM
  3. Introspection Response Signing
  4. Resource Request Signing
  5. Resource Response Signing

4 番目と 5 番目の Resource Request Signing と Resource Response Signing が FAPI 2.0 HTTP Signing のカバー範囲です。

2024 年 11 月にリリースした Authlete 3.0 (発表文書) は、FAPI 2.0 Message Signing の 5 要素を全てサポートします。ぜひ Authlete 3.0 をお試しください!→ https://console.authlete.com/

お問い合わせはこちら! → https://www.authlete.com/ja/contact/

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?