HTTP Signatures
HTTPリクエストを認証する仕組み。あるActorへのメッセージングの出所が正しいことを証明するために必要。 例:
@foo@ap1.example.comのinboxに@bar@ap2.example.comからのメッセージを示すActivityがやってきたとする 本当に@bar@ap2.example.comからのリクエストかどうかは、一般的なServer Side HTTPS単独では証明不能
Actorごとに鍵を持ち、HTTPリクエストに電子署名することでリクエストの出所を証明する
簡単なしくみの解説
サーバーへリクエストを送る際にActorの秘密鍵を利用して、リクエストヘッダーを署名する
ソートしたり、正規化したり、特定のリクエストヘッダーのみに絞ることで、HTTPリクエストを中継するサーバーによるリクエストヘッダーの加工耐性がある
リクエストボディを署名する際は Digest: ヘッダーを含めて署名することで間接的に署名することができる
署名に公開鍵についての情報(KeyId)を記載して、リクエスト先のサーバーはその公開鍵を用いて検証する
ハマりどころ
他のインスタンスのINBOXにPOSTするときなどはDigest:ヘッダを付けて内容の証明もする必要がある マストドンでは必須
マストドンの場合Digest: SHA-256=123456deadbeefという表現になる
文献によってsha-256だったりSHA-256だったりする・・・
いくつかの実装を見る限り、大文字で送信しているものが多い (受信する場合はどちらでも OK にしておくのが無難)
cat json.json | sha256sum | xxd -r -p | base64するとbase64形式でsha256確認できるのでデバッグにおすすめ
リクエストボディをファイルに保存する場合、末尾に改行があることで異なる結果が出るケースに注意。エディタによっては自動的にファイル末尾に改行を付加する
echoを使う場合は -n フラグで改行を無くせる
echo -n '{"hello":"world"}' | sha256sum | xxd -r -p | base64
このとき、Digest:ヘッダはdigestとして署名対象になることに注意
署名に使う(request-target)において、メソッドに続くものはURLでもパス単体でもなく$path$queryの形になる
良い例 /alice/inbox, /foo?bar=1, /
悪い例 https://example.com/alice/inbox (authority部分は不要!)
署名の検証の際は、最低限 (request-target)、host、date、digest、content-type の各ヘッダを署名対象の文字列に埋め込めるようにしておく必要がある
digest は上述の通り
content-type に言及しているところは少ないので、手抜きをしているとハマる (ちゃんと HTTP ヘッダ見ましょう)
対 Misskey (あるいは HTTP Signature 準拠の実装?) の場合、Authorization: ヘッダも必要
ヘッダの値は Signature keyId=... のように、Signature: ヘッダの値の前に Signature + 半角スペースとする
hr.icon
話題
ふぁぼん.iconなんか新しい仕様提案があるらしい
unasuke.icon ですね、 draft-cavage-http-signatures は draft-ietf-httpbis-message-signatures によってreplaceされています
このRFCのステータスはよく分からない
unasuke.icon IESG Evaluationの段階なのでまあそう遠くないうちにRFCになる、のかも?
2024年2月にRFC 9421になった🎉
windymelt.icon こいついっつもドラフトしてんな(早く完成させてくれ!!!)
基本的に手作りせずライブラリを使うのが定番
ライブラリでうまく行ったら言語ごとにここに報告して!!
unasuke.icon 乱立してる上に、どれも古いdraftを参照していてメンテされているかどうか不安
Tatamo.icon あまりいいライブラリがなかった。みんな crypto か crypto.subtle で手作りしてそう
httpbis, cavage 両方の署名に対応しているが verify が未実装
unasuke.icon Misskey側で実装が進められているやつ
nyarla.icon go だと GoToSocial が使っているこのライブラリが有力かもしれない(?) 中身がめちゃめちゃシンプルなので他の言語で実装する際の参考になるかもしれない (個人の感想です)
今だとGoToSocialは上記ライブラリのcommunity forkを使ってるみたいです
uriって書いてあるところにURIを渡すとハマる。実際はpath+queryを渡さなければならない
おさ.iconforkだけどメンテは続いている。
hr.icon
windymelt.icon 実装が難しくてぜんぜん上手くいかない!!!
mushus.icon 簡単なしくみの解説を書いてみましたが、ゆるふわな認識なので間違いがあったら訂正してください><
公開鍵の配布しているエンドポイントにはHTTP Signaturesによる検証はできない
一部のサーバーはActorのURL(?)に#main-keyみたいなフラグメントをつけているが、そのような実装だと該当のページにHTTP Signaturesを利用できなくなる
GoToSocial の場合はActorのURLと公開鍵を配布しているURLを別にすることでActorのURLにもHTTP Signaturesを使えるようにしている? 署名するヘッダーはhostとdateと(request-target)は最低限必要っぽい。
lacolaco.icon 新しい仕様の提案の中にはレスポンスに署名することを要求する Accept-Signature なるコンテンツネゴシエーションがあるらしい?
いくつかの言語の実装もここにリストされている