署名付きURLによるアクセス制御
URLのどの部分が署名に結び付くかを確認し、署名が正しいこと、期限内であること、取得を許可されることを別々に説明します。
説明
署名URLは、特定の要求に署名を付けて期限付きで使えるようにしたURLです。URLを持つ人が利用できるため、公開設定を変えずに渡せる一方、意図した相手だけが使うことをURL自体で判定するものではありません。
AWS Signature Version 4の形式で、GET /reports/a.csvへ署名します。教材用の固定キーとobjects.example.testを使い、署名対象の文字列、取得先やメソッドの変更、期限切れ、明示的拒否による結果を計算します。署名の検査とアクセス方針の検査を分けて追います。
URLのどの部分が署名に結び付くかを確認し、署名が正しいこと、期限内であること、取得を許可されることを別々に説明します。
1匿名アクセスの初期条件
public_getは匿名取得、signer_getは署名者の取得許可、explicit_denyは明示的拒否を表す固定の方針です。
cat policy.json匿名と署名者の取得条件を読む
python3 sign.py anonymous署名のない要求を評価するpublic_get=Falseなので匿名アクセスは403です。署名者には取得許可がありますが、まだその署名を付けた要求は使っていません。
2取得先と期限を含むURLを作る
11月1日10:00 UTCから600秒の署名URLを計算します。X-Amz-Dateは開始時刻、X-Amz-Expiresは秒数です。
python3 sign.py makeGETの取得先と600秒の期限に署名するパスは/reports/a.csv、署名対象のヘッダーはhostです。X-Amz-Credentialには教材用のアクセスキー識別子と日付・region・serviceの範囲が入り、最後にX-Amz-Signatureが付きます。秘密の署名鍵そのものをURLへ入れる方式ではありません。
3署名に使った正規化文字列を読む
Canonical Requestを表示します。メソッド、パス、並べたクエリ、host、署名ヘッダー名、本文の扱いが改行で結ばれています。
python3 sign.py canonical署名対象の文字列とSHA-256を表示する先頭はGETと/reports/a.csvです。クエリには期限やCredentialを含め、計算結果であるX-Amz-Signature自身は含めません。UNSIGNED-PAYLOADは本文のハッシュを署名へ固定しない形式で、URLの他の項目まで未署名という意味ではありません。
4期限内の同じ要求を評価
10:05 UTCに同じGETを評価します。
python3 sign.py get同じURLを期限内の時刻で評価するsignature_valid=True、age_seconds=300、status=200です。署名、経過時間、署名者の許可がそろっています。署名URLを使ったことは、匿名アクセスを許可する設定変更ではありません。
5パスだけを書き換える
署名クエリを残したまま、パスを/reports/b.csvへ変えて評価します。
python3 sign.py tamper署名を残して取得先だけを変える
python3 sign.py get --file tampered.txt変更後のURLを検査するsignature_valid=FalseでSignatureDoesNotMatchです。a.csv向けの署名を、そのまま別オブジェクトの取得へ使うことはできません。
6GETの署名を別メソッドで使う
元のURLに対してPUTを指定します。
python3 sign.py get --method PUT同じURLを異なるHTTPメソッドとして検査するメソッドもCanonical Requestの一部なので、署名は一致しません。同じパスとクエリでも、GETの署名が書込みの許可へ変わるわけではありません。
7正しい署名でも期限を超える場合
開始から601秒後の10:10:01 UTCで評価します。
python3 sign.py get --at 2026-11-01T10:10:01+00:00600秒の期限を過ぎた時刻で評価する署名自体はTrueですが、age_seconds=601でExpiredになります。バイト列として正しい署名と、現在使える要求であることは別の条件です。
8期限内でも明示的拒否は優先される
取得を明示的に拒否する方針へ替え、期限内の10:05で元のURLを評価します。
cp policy-deny.json policy.json取得への明示的拒否を適用する
python3 sign.py get元のURLを期限内に再評価するsignature_valid=TrueでもAccessDeniedです。署名を持っていることは、現在適用される拒否条件を無効にする意味ではありません。
9公開設定を変える場合との違い
比較のため、匿名取得を許可し明示的拒否を除いた方針へ替えます。
cp policy-public.json policy.json匿名取得を許可する方針へ替える
python3 sign.py anonymous署名のない取得が変わったことを確認する匿名でも200になります。これは署名URLの配布とは異なり、署名のない取得を許可する方針です。対象や期限を限定して渡す方法と、匿名取得を許可する設定を混同しないようにします。
まとめ
署名はメソッド・パス・クエリ・対象ヘッダーなどの要求内容に対応します。
署名の一致、期限、アクセス方針をすべて確認します。
署名URLを渡すことと、匿名取得を許可する公開設定は別です。
操作履歴と解説を振り返れます。
シェル / Python 3
cat policy.json
python3 sign.py anonymous{
"public_get": false,
"signer_get": true,
"explicit_deny": false
}
status=403python3 sign.py makehttps://objects.example.test/reports/a.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIDEXAMPLE%2F20261101%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20261101T100000Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=16095e531e3c7d1f2eba0fdee87b7e8b5b84dd040e312026286acbce5b8b3474
python3 sign.py canonicalGET /reports/a.csv X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIDEXAMPLE%2F20261101%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20261101T100000Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host host:objects.example.test host UNSIGNED-PAYLOAD canonical_sha256=da46b27fd066f280bbb6e150273df52ab8f1823f7374f8c23062507e1fe56195
python3 sign.py getsignature_valid=True age_seconds=300 status=200 reason=OK
python3 sign.py tamper
python3 sign.py get --file tampered.txtpath changed to /reports/b.csv; query signature kept signature_valid=False age_seconds=300 status=403 reason=SignatureDoesNotMatch
python3 sign.py get --method PUTsignature_valid=False age_seconds=300 status=403 reason=SignatureDoesNotMatch
python3 sign.py get --at 2026-11-01T10:10:01+00:00signature_valid=True age_seconds=601 status=403 reason=Expired
cp policy-deny.json policy.json
python3 sign.py getsignature_valid=True age_seconds=300 status=403 reason=AccessDenied
cp policy-public.json policy.json
python3 sign.py anonymousstatus=200
演習完了