60. AWSへのリクエストは毎回署名される — SigV4のHMAC導出チェーン
AWSにAPIリクエストを送るたび、シークレットキーは一度もネットワークに流れないのに「本人だ」と証明できます。その仕組みを、リクエストの正規化からHMACの鍵導出チェーンまで図で追っていきます。
① シークレットキーは送らない
- 公式の言い回し「You don't use your secret access key to sign API requests. Instead, you use the SigV4 signing process.」— シークレットキーそのものではなく、それを使った導出結果で署名する。
- 「AWS then replicates this process and verifies the signature, granting or denying access accordingly.」— AWS側は同じ計算を再現して照合する。
SigV4は「秘密を送らずに、秘密の所持を証明する」仕組みで、クライアントとAWSが同じシークレットキーで同じ計算を再現し、結果の一致を鍵の所持証明とします(チャレンジレスポンス認証に通じる発想)。API Gateway編で見たTLSが「通信路そのもの」を暗号化するのに対し、SigV4はリクエスト1本ごとに誰が出したか(認証)と中身が変わっていないか(完全性)を保証し、両者は重ねて使われます。
② リクエストを正規化する(canonical request)
- 6要素は改行(\n)で連結する: HTTPMethod \n CanonicalURI \n CanonicalQueryString \n CanonicalHeaders \n SignedHeaders \n HashedPayload。
- ヘッダ名は小文字化・辞書順、値は前後空白trim・連続空白を1つに。必須は host(HTTP/1.1)または :authority(HTTP/2)と、リクエストに含める全ての x-amz-* ヘッダ。Content-Type ヘッダが存在する場合はそれも含める必要がある。
- クエリ文字列はキー名で辞書順ソート(エンコード後にソート)。
- 本文が無いGETなどは空文字のハッシュ Hex(SHA256Hash("")) を使う。
- S3は本文ハッシュを載せる x-amz-content-sha256 ヘッダが必須(本文が無ければ空文字のハッシュ)。
「揃った1つの形」に直す(正規化)のは、クライアントとAWSが別々に計算しても同じ入力から始めるためで、最後にリクエスト全体をSHA-256で1本のハッシュに凝縮します。SHA-256はKinesis/FIFO編で重複排除に登場した「同じ入力なら必ず同じ出力、1文字でも変われば全く別の値」のハッシュで、その性質をここでは「中身が1バイトでも書き換わったら別のハッシュになる」改ざん検知に使います。
③ string to sign を組み立てる
- アルゴリズム名はSigV4で AWS4-HMAC-SHA256(SigV4aは AWS4-ECDSA-P256-SHA256)。
- credential scopeのregion/service/終端文字列は小文字。終端文字列は固定で aws4_request。
- 日時は現在のUTC時刻をISO 8601形式(例 20130524T000000Z)。
- credential scopeは「resulting signatureを指定Regionとserviceに限定する」ためのもの、と公式が明記。
string to signは「これから署名する内容」を1つの文字列にまとめたものです。日時とcredential scope(日付/リージョン/サービス)を含めることで、この署名が「いつ・どこ・どのサービス向け」かがそのまま署名対象に焼き込まれます。
④ 署名キーをHMACの連鎖で導出する
- 各段は「前段の出力を鍵、次の要素をデータ」としてHMAC-SHA256を掛ける連鎖。公式:「The result of each call to the hash function becomes the input for the next call.」
- 初段の鍵だけが "AWS4" + シークレットキーの連結。以降は前段の出力が鍵になる。
- 最終段のデータは固定文字列 "aws4_request"。導出に使う入力は日付(YYYYMMDD)・リージョンコード(例 us-east-1)・サービスコード(例 ec2)。
- HMAC(鍵付きハッシュ / メッセージ認証コード)が暗号基礎の新顔。②のSHA-256は「鍵なしハッシュ」で誰でも同じ値を計算できるが、HMACは鍵を知る者しか正しい値を出せない。「鍵を持っていることの証明」と「中身が変わっていないことの証明」を同時に成立させ、SigV4はそれを鍵の導出にも最終署名にも使う。
シークレットキーそのものは一度もHMACの「データ」側に置かず、最初の鍵材料として使うだけです。日付→リージョン→サービスと段階的に絞り込むため、出来上がるSigningKeyは「その日・そのリージョン・そのサービス」専用——万一漏れても翌日には無効で、他リージョン・他サービスには効かず、被害がスコープに限定されるのがこのチェーンの意味です。
⑤ 署名を計算してAuthorizationヘッダに載せる
- 最終署名は signature = HMAC-SHA256(SigningKey, string to sign)。SigV4の「最終署名はHMAC-SHA256」と公式が明記。結果はbinary→小文字16進。
- Authorizationヘッダはアルゴリズム名とCredentialの間にカンマ無し、それ以外はカンマ区切り。
- 認証情報はAuthorizationヘッダ、またはURLのクエリ文字列(X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature 等)でも表現できる。
- 一時認証情報(STS発行)のときは X-Amz-Security-Token にセッショントークンを載せる。サービスによっては canonical request に含める必要があり、別のサービスでは署名計算後に付けるだけでよい——要件はサービスごとに確認。
SigningKeyでstring to signをHMAC-SHA256すれば署名の完成です。AWS側はリクエストに含まれるCredential情報から同じSigningKeyを再導出し、同じstring to signで署名を再計算して照合——一致すれば通し、しなければ拒否します。
⑥ 署名が守る3つのこと と SigV4a
- リプレイ防止: 公式「In most cases, a request must reach AWS within five minutes of the time stamp in the request. Otherwise, AWS denies the request.」= 原則、タイムスタンプから5分以内にAWSへ到達する必要がある。
- 改ざん防止: 「some of the request elements are used to calculate a hash (digest) of the request」— リクエスト要素からハッシュを作り照合、不一致なら拒否。
- なりすまし防止: 署名はアクセスキー(アクセスキーID+シークレットアクセスキー)で作る。一時認証情報ならセキュリティトークンも必要。
- SigV4a: シークレットアクセスキーからECDSA(NIST P-256)の鍵ペアを導出。AWSは公開鍵のみ保管すればよく、公開鍵では署名を作れない。SDK/CLIがマルチリージョン署名を要する機能(S3マルチリージョンアクセスポイント等)を呼ぶと自動でSigV4aに切り替わる。アルゴリズム名は AWS4-ECDSA-P256-SHA256。
1本の署名が、鍵の所持証明(なりすまし防止)・リクエスト要素のハッシュ(改ざん防止)・タイムスタンプの5分失効(リプレイ防止)を同時に成立させます。SigV4aはこの発想を非対称鍵に拡張し、1つの署名を複数リージョンで検証できるようにしたもので、本編では位置づけの紹介にとどめます。