一覧CL-06-2 · クラウドストレージ

署名付き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

1
cat policy.json python3 sign.py anonymous
{
  "public_get": false,
  "signer_get": true,
  "explicit_deny": false
}
status=403
2
python3 sign.py make
https://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
3
python3 sign.py canonical
GET
/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
4
python3 sign.py get
signature_valid=True age_seconds=300 status=200 reason=OK
5
python3 sign.py tamper python3 sign.py get --file tampered.txt
path changed to /reports/b.csv; query signature kept
signature_valid=False age_seconds=300 status=403 reason=SignatureDoesNotMatch
6
python3 sign.py get --method PUT
signature_valid=False age_seconds=300 status=403 reason=SignatureDoesNotMatch
7
python3 sign.py get --at 2026-11-01T10:10:01+00:00
signature_valid=True age_seconds=601 status=403 reason=Expired
8
cp policy-deny.json policy.json python3 sign.py get
signature_valid=True age_seconds=300 status=403 reason=AccessDenied
9
cp policy-public.json policy.json python3 sign.py anonymous
status=200

演習完了