REST リクエストの署名と認証
トピック
注記
このトピックでは、署名バージョン 2 を使用してリクエストを認証する方法を説明します。Amazon S3 では、現在、最新の署名バージョン 4 がサポートされています。この最新の署名バージョンはすべてのリージョンでサポートされており、2014 年 1 月 30 日以降は、新しいリージョンではバージョン 4 の署名のみがサポートされます。詳細については、Amazon Simple Storage Service API リファレンスの「リクエストの認証 (AWS 署名バージョン 4)」を参照してください。
認証とは、自身のアイデンティティをシステムに証明するプロセスのことをいいます。アイデンティティは、Amazon S3 のアクセス管理の判断における重要な要因です。リクエストの許可または拒否は、リクエスタのアイデンティティに部分的に基づいています。たとえば、バケットを作成する権利は、登録開発者用に予約されています。また、バケットにオブジェクトを作成する権利は、対象のバケット所有者用に (デフォルトで) 予約されています。開発者としてこれらの権限を実行するリクエストを行うので、そのリクエストを認証することで、自身のアイデンティティをシステムに対して証明する必要があります。このセクションでは、その方法を説明します。
注記
このセクションの内容は HTTP POST には適用されません。詳細については、「POST (AWS 署名バージョン 2) を使用したブラウザベースのアップロード」を参照してください。
Amazon S3 の REST API では、キーを使用して生成される HMAC (ハッシュメッセージ認証コード) に基づくカスタムの HTTP スキーマが認証に使用されます。リクエストを認証するには、まず、選択されているリクエストの要素を連結し、文字列を作成します。次に、AWS シークレットアクセスキーを使用して、その文字列の HMAC を計算します。正式な呼び名ではありませんが、このプロセスのことを「リクエストへの署名」と呼び、HMAC アルゴリズムの出力を署名と呼びます。これは、このプロセスが実際の署名のセキュリティプロパティを真似ているからです。最後に、このセクションで説明する構文を使用して、この署名をリクエストのパラメータとして追加します。
システムは、認証済みリクエストを受け取るとき、リクエスト送信者が所有している AWS シークレットアクセスキーを取得し、同様の方法でそのアクセスキーを使用して、受信したメッセージの署名を計算します。そして、計算した署名と、リクエスタから提示された署名を比較します。2 つの署名が一致したら、リクエスタは AWS シークレットアクセスキーにアクセスできると判断されるため、システムはそのキーの発行先となるプリンシパルの権限をサポートします。2 つの署名が一致しない場合、リクエストは中断し、エラーメッセージが返されます。
例 認証された Amazon S3 の REST リクエスト
GET /photos/puppy.jpg HTTP/1.1 Host: awsexamplebucket1.us-west-1.s3.amazonaws.com Date: Tue, 27 Mar 2007 19:36:42 +0000
Authorization: AWS AKIAIOSFODNN7EXAMPLE: qgk2+6Sv9/oM7G3qLEjTH1a1l1g=
一時的なセキュリティ認証情報の使用
一時的なセキュリティ認証情報を使用してリクエストに署名する場合は (「リクエストの実行」を参照)、x-amz-security-token
ヘッダーを追加して、対応するセキュリティトークンをリクエストに含める必要があります。
AWS Security Token Service API を使用して一時的なセキュリティ認証情報を取得すると、そのレスポンスには、一時的なセキュリティ認証情報とセッショントークンが含まれます。リクエストを Amazon S3 に送信するときに、x-amz-security-token
ヘッダーでセッショントークン値を指定します。IAM が提供する AWS Security Token Service API については、AWS Security Token Service API リファレンスガイドの「アクション」を参照してください。
認証ヘッダー
Amazon S3 の REST API は、標準の HTTP ヘッダーの Authorization
を使用して認証情報を渡します。(標準ヘッダーの名前とは異なり、ヘッダーに含まれるのは承認ではなく認証情報です。) Amazon S3 の認証スキームにおける Authorization ヘッダーの形式は次のとおりです。
Authorization: AWS
AWSAccessKeyId
:Signature
デベロッパーには、登録時に AWS アクセスキー ID と AWS シークレットアクセスキーが発行されます。リクエストを認証するために、AWSAccessKeyId
要素は、署名の計算に使用されたアクセスキー ID を識別するほか、リクエストを行っている開発者も間接的に識別します。
Signature
要素は、リクエストから選択した要素の RFC 2104HMAC-SHA1 です。したがって、Authorization ヘッダーの Signature
部分はリクエストによって異なります。システムによって計算されたリクエストの署名が、リクエストに含まれる Signature
と一致する場合は、リクエスタが AWS シークレットアクセスキーを所有していることになります。その後、リクエストは、キーの発行対象者である開発者のアイデンティティと権限に従って処理されます。
以下は擬似文法で、Authorization
リクエストヘッダーの構文例を示しています。(例中の \n
は Unicode のコードポイント U+000A
を意味しています。これは通常改行と呼ばれています)。
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature; Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of(YourSecretAccessKey), UTF-8-Encoding-Of( StringToSign ) ) ); StringToSign = HTTP-Verb + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedAmzHeaders + CanonicalizedResource; CanonicalizedResource = [ "/" + Bucket ] + <HTTP-Request-URI, from the protocol name up to the query string> + [ subresource, if present. For example "?acl", "?location", or "?logging"]; CanonicalizedAmzHeaders = <described below>
HMAC-SHA1 は、RFC 2104 の Keyed-Hashing for Message AuthenticationYourSecretAccessKey
) をキーとして使用し、StringToSign
の UTF-8 エンコーディングをメッセージとして使用します。また、HMAC-SHA1 の出力もバイト文字列で、これはダイジェストと呼ばれます。Signature
リクエストパラメータは、このダイジェストをエンコードする Base64 によって作成されます。
署名のためのリクエストの標準化
既に説明したように、システムは認証済みリクエストを受け取るときに、計算されたリクエスト署名と StringToSign
のリクエストで指定された署名を比較します。そのため、署名は Amazon S3 で使用されているものと同じ方法で計算する必要があります。署名のためにリクエストを承認済み形式にするプロセスは正規化と言います。
CanonicalizedResource 要素の作成
CanonicalizedResource
は、リクエストの対象となる Amazon S3 のリソースを表します。REST リクエストのこのリソースは次のように作成します。
1 |
空の文字列 ( |
2 |
HTTP ホストヘッダー (仮想ホスト形式) を使用してバケットが指定されているリクエストについては、バケット名の前に 仮想ホスティング形式のリクエスト「https://awsexamplebucket1.s3.us-west-1.amazonaws.com/photos/puppy.jpg」の場合、 パス形式のリクエスト「https://s3.us-west-1.amazonaws.com/awsexamplebucket1/photos/puppy.jpg」の場合、 |
3 |
デコードされていない HTTP リクエスト URI のパス部分を追加します (クエリ文字列まで、ただし、その文字列は含みません)。 仮想ホスト形式のリクエスト「https://awsexamplebucket1.s3.us-west-1.amazonaws.com/photos/puppy.jpg」の場合、 パス形式のリクエスト「https://s3.us-west-1.amazonaws.com/awsexamplebucket1/photos/puppy.jpg」の場合、 GET サービスなどのバケットを指定しないリクエストの場合は、「/」を追加します。 |
4 |
CanonicalizedResource 要素を作成するときに含める必要があるサブリソースは、acl、lifecycle、location、logging、notification、partNumber、policy、requestPayment、uploadId、uploads、versionId、versioning、versions、および website です。 リクエストでレスポンスヘッダーの値を上書きするクエリ文字列パラメータを指定する場合 (「GetObject」を参照) は、クエリ文字列パラメータとその値を追加します。これらのパラメータ値は署名時にエンコードする必要はありませんが、リクエストの実行時にはエンコードする必要があります。GET リクエストのクエリ文字列パラメータには、 複数のオブジェクトの Delete リクエストに対して CanonicalizedResource を作成する際には、 |
HTTP リクエスト URI の CanonicalizedResource の要素は、URL エンコーディングメタ文字を含め、HTTP リクエストに表示されるとおりに署名する必要があります。
CanonicalizedResource
は、HTTP リクエスト URI とは異なる場合があります。特に、リクエストが HTTP Host
ヘッダーを使用してバケットを指定する場合、そのバケットは HTTP リクエスト URI に表示されませんが、CanonicalizedResource
にもバケットが含まれます。クエリ文字列パラメータは、リクエスト URI に表示される可能性がありますが、CanonicalizedResource
には含まれません。詳細については、「バケットの仮想ホスティング」を参照してください。
CanonicalizedAmzHeaders 要素の作成
StringToSign
の CanonicalizedAmzHeaders 部分を作成するには、次の手順に従って、「x-amz-」で始まるすべての HTTP リクエストヘッダーを選択します (大文字と小文字は区別されません)。
1 | 各 HTTP ヘッダー名を小文字に変換します。たとえば、「X-Amz-Date 」は「x-amz-date 」に変換します。 |
2 | ヘッダーのコレクションを辞書と同じ順序でヘッダー名ごとに並べ替えます。 |
3 | RFC 2616 のセクション 4.2 の説明に従って、同じ名前のヘッダーフィールドを 1 つの「ヘッダー名:カンマ区切りの値のリスト」にまとめます。値の間にスペースは入れません。たとえば、2 つのメタデータヘッダー「x-amz-meta-username: fred 」および「x-amz-meta-username: barney 」が 1 つのヘッダーに結合されると、「x-amz-meta-username:
fred,barney 」になります。 |
4 | (RFC 2616 のセクション 4.2 で許可されている) 複数行にわたる長いヘッダーの折り返しのスペース (改行を含む) を 1 つのスペースに置き換えて、折り返しをなくします。 |
5 | ヘッダーのコロンの前後のスペースをすべて削除します。たとえば、ヘッダー「x-amz-meta-username: fred,barney 」は「x-amz-meta-username:fred,barney 」になります。 |
6 | 最後に、改行文字 (U+000A ) を、結果の一覧の各標準化ヘッダーに追加します。この一覧のすべてのヘッダーを 1 つの文字列に連結することで、CanonicalizedResource 要素を作成します。 |
位置ヘッダーおよび指定 HTTP ヘッダーの StringToSign 要素
StringToSign
の最初のヘッダー要素 (Content-Type、Date、および Content-MD5) は位置を示します。StringToSign
には、これらのヘッダーの名前は含まれません。含まれるのはリクエストの値のみです。一方、「x-amz-
」要素には名前が付いています。ヘッダーの名前と値は両方とも StringToSign
に含まれます。
StringToSign
の定義で呼び出される位置ヘッダーがリクエストにない場合は (たとえば、Content-Type
または Content-MD5
は、PUT リクエストのオプションであり、GET リクエストには無意味です)、空の文字列 ("") をその位置に代入します。
タイムスタンプの要件
認証済みリクエストには有効なタイムスタンプ (HTTP Date
ヘッダーまたは x-amz-date
代替) が必ず必要です。また、認証済みリクエストに含まれているクライアントのタイムスタンプは、リクエストの受信時の Amazon S3 システムの時刻から 15 分以内である必要があります。そうでない場合、リクエストは失敗し、RequestTimeTooSkewed
エラーコードが返されます。このように制限することで、攻撃者によって傍受されたリクエストが繰り返される可能性を限定します。傍受に対する保護をさらに強化するには、認証済みリクエストに対して HTTPS 転送を使用します。
注記
リクエスト日の検証の制約は、クエリ文字列認証を使用しない認証済みリクエストにのみ適用されます。詳細については、「クエリ文字列による代替リクエスト認証」を参照してください。
HTTP クライアントライブラリによっては、リクエストの Date
ヘッダーを設定する機能が公開されていないことがあります。標準化ヘッダーの「Date」ヘッダーの値を含めることができない場合は、代わりに「x-amz-date
」ヘッダーを使用してリクエストのタイムスタンプを設定します。x-amz-date
ヘッダーの値は、RFC 2616 (http://www.ietf.org/rfc/rfc2616.txtx-amz-date
ヘッダーがリクエストに存在する場合は、すべての Date
ヘッダーがリクエスト署名の計算時に無視されます。したがって、x-amz-date
ヘッダーを含める場合、Date
を作成するときは、空の文字列を StringToSign
に対して使用します。例については、次のセクションを参照ください。
認証の例
このセクションの例では、次の表の証明書 (実際は機能していません) を使用しています。
パラメータ | 値 |
---|---|
AWSAccessKeyId | AKIAIOSFODNN7EXAMPLE |
AWSSecretAccessKey | wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY |
StringToSign
の例では、書式設定はそれほど複雑ではありません。\n
は Unicode コードポイント U+000A
を意味します。これは一般的には改行と呼ばれています。また、例では「+0000」を使用してタイムゾーンを指定しています。代わりに「GMT」を使用してタイムゾーンを指定することもできますが、署名はこの例に示したものとは異なることになります。
オブジェクト GET
この例では、awsexamplebucket1 バケットからオブジェクトを取得します。
リクエスト | StringToSign |
---|---|
|
|
バケット名は CanonicalizedResource には含まれますが、HTTP リクエスト URI には含まれないことに注意してください。(これはホストヘッダーによって指定されます。)
注記
以下の Python スクリプトは、渡されたパラメータを使用して、先程の署名を計算します。このスクリプトを使用し、必要に応じてキーおよび StringToSign を置き換えて、独自の署名を作成できます。
import base64 import hmac from hashlib import sha1 access_key = '
AKIAIOSFODNN7EXAMPLE
'.encode("UTF-8") secret_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
'.encode("UTF-8") string_to_sign = 'GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/awsexamplebucket1/photos/puppy.jpg
'.encode("UTF-8") signature = base64.b64encode( hmac.new( secret_key, string_to_sign, sha1 ).digest() ).strip() print(f"AWS {access_key.decode()}:{signature.decode()}")
Object PUT
この例では、オブジェクトを awsexamplebucket1 バケットに配置します。
リクエスト | StringToSign |
---|---|
|
|
リクエスト内および StringToSign 内の Content-Type ヘッダーに注意してください。また、Content-MD5 はリクエストにないので、StringToSign では空白のままであることに注意してください。
リスト
この例では、awsexamplebucket1 バケットのコンテンツのリストを取得します。
リクエスト | StringToSign |
---|---|
|
|
CanonicalizedResource の後ろにスラッシュがあること、そしてクエリ文字列パラメータがないことに注意してください。
Fetch
この例では、「awsexamplebucket1」バケットのアクセスコントロールポリシーのサブリソースを取得します。
リクエスト | StringToSign |
---|---|
|
|
サブリソースクエリ文字列パラメータがどのように CanonicalizedResource に挿入されているかに注意してください。
削除
この例では、パス形式および Date 代替を使用して、「awsexamplebucket1」バケットからオブジェクトを削除します。
リクエスト | StringToSign |
---|---|
|
|
代替の「x-amz-date」方式を使用して日付を指定する方法に注意してください (クライアントライブラリにより、日付を設定できない場合など)。このような場合、x-amz-date
が Date
ヘッダーより優先されます。このため、署名内の日付エントリに x-amz-date
ヘッダーの値を含める必要があります。
アップロード
この例では、オブジェクトを、メタデータが含まれる CNAME スタイルの仮想ホストバケットにアップロードします。
リクエスト | StringToSign |
---|---|
|
|
「x-amz-」ヘッダーが並べ替えられ、スペースが削除されて小文字に変換されていることに注意してください。また、同じ名前を持つ複数のヘッダーが値の区切りとしてカンマを使用して結合されている様子も確認してください。
Content-Type
および Content-MD5
HTTP エンティティヘッダーのみが StringToSign
に表示されていることに注意してください。もう一方の Content-*
エンティティヘッダーは表示されていません。
また、バケット名は CanonicalizedResource
には含まれますが、HTTP リクエスト URI には含まれないことに注意してください。(これはホストヘッダーによって指定されます。)
自分のすべてのバケットのリストを取得する
リクエスト | StringToSign |
---|---|
|
|
Unicode キー
リクエスト | StringToSign |
---|---|
|
|
注記
リクエスト URI から派生した StringToSign
の要素は、URL エンコーディングおよび大文字/小文字の設定を含め、文字通りに取得されます。
REST リクエストの署名に関する問題
REST リクエスト認証が失敗すると、システムは、そのリクエストに対して XML エラードキュメントで応答します。このエラードキュメントに含まれる情報は、開発者が問題を診断するのに役立ちます。特に、StringToSign
エラードキュメントの SignatureDoesNotMatch
要素を確認すると、システムで使用されているリクエスト標準化が正確にわかります。
ツールキットの中には、知らないヘッダーを通知なしで事前に挿入するものがあります。たとえば、PUT の実行中にヘッダー Content-Type
が追加されることがあります。この場合、挿入されたヘッダーの値は一定であることがほとんどで、Ethereal、tcpmon などのツールを使用すると、不足しているヘッダーを検出できます。
クエリ文字列による代替リクエスト認証
Authorization
HTTP ヘッダーを使用する代わりに、必要な情報をクエリ文字列パラメータとして渡すことで、特定の種類のリクエストを認証できます。これは、サードパーティーのブラウザで、リクエストのプロキシを行わずにプライベートの Amazon S3 データに直接アクセスさせる場合に便利です。これを行うには、「署名付き」のリクエストを作成し、エンドユーザーのブラウザが取得できる URL としてエンコードします。さらに、署名付きのリクエストは、有効期限を指定することで制限できます。
クエリパラメータを使用するリクエストの認証の詳細については、Amazon Simple Storage Service API リファレンスの「リクエストの認証: クエリパラメータの使用 (AWS 署名バージョン 4)」を参照してください。AWS SDK を使用して署名付き URL を生成する例については、署名付き URL を使用したオブジェクトの共有 を参照してください。
署名の作成
クエリ文字列で認証済みの Amazon S3 の REST リクエストの例を次に示します。
GET /photos/puppy.jpg ?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Expires=1141889120&Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D HTTP/1.1 Host: awsexamplebucket1.s3.us-west-1.amazonaws.com Date: Mon, 26 Mar 2007 19:37:58 +0000
クエリ文字列リクエスト認証方法には、特別な HTTP ヘッダーは必要ありません。代わりに、必要な認証要素は、クエリ文字列パラメータとして指定します。
クエリ文字列パラメータ名 | 値の例 | 説明 |
---|---|---|
AWSAccessKeyId |
AKIAIOSFODNN7EXAMPLE |
AWS アクセスキー ID。リクエストへの署名に使用する AWS シークレットアクセスキーを指定します。また、リクエストを行うデベロッパーのアイデンティティを間接的に指定します。 |
Expires |
1141889120 |
署名の有効期限。エポック (1970 年 1 月 1 日の 00:00:00 UTC) からの時間が秒単位で指定されます。この時刻 (サーバーの時刻) より後に受信したリクエストは拒否されます。 |
Signature |
vjbyPxybdZaNmGa%2ByT272YEAiv4%3D |
StringToSign の HMAC-SHA1 の Base64 エンコーディングの URL エンコーディング。 |
クエリ文字列リクエスト認証方法は、通常の方法とは若干異なりますが、Signature
リクエストパラメータおよび StringToSign
要素の形式だけが異なります。クエリ文字列リクエスト認証方法を示す疑似文法を次に示します。
Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) ) ); StringToSign = HTTP-VERB + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Expires + "\n" + CanonicalizedAmzHeaders + CanonicalizedResource;
YourSecretAccessKey
は、アマゾン ウェブ サービスのデベロッパーとしてサインアップするときに、Amazon によって割り当てられる AWS シークレットアクセスキー ID です。Signature
が、クエリ文字列内に適切に配置されるために URL エンコードされている点に注目してください。また、HTTP では StringToSign
であった位置要素が、Date
では Expires
に置き換えられていることに注意してください。CanonicalizedAmzHeaders
および CanonicalizedResource
は同じです。
注記
クエリ文字列認証メソッドでは、署名する文字列を計算する際に、Date
または x-amz-date request
ヘッダーを使用しません。
クエリ文字列リクエスト認証
リクエスト | StringToSign |
---|---|
|
|
ブラウザが GET リクエストを行うとき、Content-MD5 または Content-Type ヘッダーを提供しないこと、また、x-amz- ヘッダーを設定しないことが前提となっています。したがって、StringToSign
のこの部分は空白のままです。
Base64 エンコーディングの使用
HMAC リクエスト署名は、Base64 エンコードされている必要があります。Base64 エンコーディングでは、署名を、リクエストにアタッチできるシンプルな ASCII 文字列に変換します。プラス (+)、スラッシュ (/)、等号 (=) などの署名文字列に含めることができる文字は、URI で使用する場合には、エンコードされている必要があります。たとえば、認証コードにプラス記号 (+) が含まれる場合は、リクエストで %2B としてエンコードします。スラッシュは %2F、等号は %3D でエンコードします。
Base64 エンコーディングの例については、Amazon S3 の「認証の例」を参照してください。