HTTP Message Signaturesとは?
HTTP リクエストや HTTP レスポンス (以降まとめて『HTTP メッセージ』と呼びます) に署名する方法は数多く提案されてきました。それらは実装され、実際に運用もされました。しかし、いずれもデファクトスタンダードの地位を獲得できず、長い年月が過ぎました。
そのような歴史を経てようやく、一つの方法が IETF の RFC として承認されました。それが RFC 9421: HTTP Message Signatures です。
この仕様に従って署名された HTTP メッセージには Signature
、Signature-Input
という二つの HTTP フィールドが含まれます。Signature
フィールドには署名が、Signature-Input
フィールドには署名に関するメタデータが含まれます。
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 8102、Errata 8103 として報告しました。簡単に言うと、仕様書内で @signature-input
となっている箇所は、正しくは @signature-params
です。
FAPI 2.0 HTTP Signingとは?
OAuth 2.0 のリソースサーバとやりとりする HTTP メッセージに対して HTTP Message Signatures を適用する方法を定めた仕様が FAPI 2.0 HTTP Signing です。
この仕様の要点は次の通りです。
リソースリクエストに対する署名 |
|
リソースレスポンスに対する署名 |
|
現在の 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
スクリプトはcreated
やtag
といった署名パラメータがアルファベット順に並んでいることを想定している。リソースサーバの実装が異なる順番を用いる場合、このスクリプトによる署名検証は失敗する。
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 つの要素で構成されます。
- Authorization Request Signing - JAR
- Authorization Response Signing - JARM
- Introspection Response Signing
- Resource Request Signing
- 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/