LAC WATCH

セキュリティとITの最新情報

RSS

株式会社ラック

メールマガジン

サイバーセキュリティや
ラックに関する情報をお届けします。

ラックピープル | 

AWSの機能を活用するために押さえておきたい"HTTPリクエスト"のはなし

AWSはインターネット上でさまざまなコンピュータリソース(サーバ、データベース、ストレージ、ネットワーキングなど)を提供し、企業や個人がそれらのリソースを利用してアプリケーションやサービスを構築・実行することを可能にします。市場シェアが非常に大きく、幅広いサービスと豊富なエコシステムを備えているため、多くのユーザにとって重要なクラウドプラットフォームです。

AWSでAPIを実行するときは、リクエストが正しいユーザや形式なのかを判断する署名が必要です。この署名には、SignatureV4(Signature Version 4)と呼ばれる方式が採用されています。

今回は、SignatureV4の署名を使用する、HTTPリクエストの内容を説明します。AWS APIを実行するHTTPリクエストを自動で生成し、生成されたHTTPリクエストの詳細の順に見ていきたいと思います。

AWS APIを実行するHTTPリクエストの自動生成

対象のAPI

AWS Price List APIのDescireServicesを使用します。
以下のAWS CLIで使用するAPIと同じAPIを利用します。

# AWSサービスの料金を取得するAPI(長いので省略)
$ aws pricing describe-services --region='us-east-1'

HTTPリクエストを自動生成

Web APIテストツールであるPostmanを使用し、HTTPリクエストを自動生成します。
PostmanのAWS SignatureV4実行機能を利用して署名の計算をさせます。

1.Postmanを起動し、リクエストを作成

Postmanを起動し、リクエストを作成

リクエストには以下を設定します。

メソッド: POST
URL: https://api.pricing.us-east-1.amazonaws.com
Authorizationタブ:
 Type: AWS Signature
 AccessKey: 
 SecretKey: 
 AWS Region: us-east-1
 Service Name: pricing
Headersタブ:
 X-Amz-Target: AWSPriceListService.DescribeServices
 Content-Type: application/x-amz-json-1.1
Body:
{}

2.メソッドをPOSTに変更し、URLを記載

メソッドをPOSTに変更し、URLを記載

3.Authorizationタブに移動し、Typeで"AWS Signature"を選択

Authorizationタブに移動し、Typeで

4.ADVANCEDも含めて記載

ADVANCEDも含めて記載

5.Headersタブに移動し、ヘッダを2つ追加

Headersタブに移動し、ヘッダを2つ追加

6.Bodyタブに移動し、rawを選択

空のJSON({})をBodyに入力します。

Bodyタブに移動し、rawを選択。空のJSON({})をBodyに入力

7.Sendを押して実行

Responseや200 OKが返ってくれば、成功です。

Sendを押して実行し、Responseや200 OKが返ってくれば、成功

8.HTTPリクエスト確認

Consoleウィンドウで、「show raw log」を表示することで、HTTPリクエストなどを表示できます。

HTTPリクエスト確認
POST / HTTP/1.1
X-Amz-Target: AWSPriceListService.DescribeServices
Content-Type: application/x-amz-json-1.1
X-Amz-Content-Sha256: 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
Host: api.pricing.us-east-1.amazonaws.com
X-Amz-Date: 20230113T080428Z
Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20230113/us-east-1/pricing/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target, Signature=0cae2bc25fc75c011f44cd9a4d4aaee0f19fd74e5545d5eb9c4b2e7e38081359
User-Agent: PostmanRuntime/7.30.0
Postman-Token: c5ee81ab-897f-4de9-b4f9-4f883d44d30f
Content-Length: 2
 
{}

実行履歴確認

APIが実行できたので、CloudTrailで実行履歴を確認します。
AWSのマネジメントコンソールを起動し、バージニア北部(us-east-1)に移ります。
"イベント履歴"を開き、イベント名"DescribeServices"でフィルタします。

実行したAPIが履歴に記録されています。

実行したAPIがイベント履歴に記録されている

Postmanでは、いくつかのヘッダを自動生成します。
Headersタブで、hiddenを押すと自動生成のヘッダが確認でき、User-AgentにPostmanが設定されています。

Postmanでいくつかのヘッダを自動生成する

イベントを確認すると、"userAgent"の箇所が、Postmanになっていることが確認できます。

userAgentの箇所が、Postmanになっている

これで、APIへのリクエストに使用するHTTPリクエストの自動生成ができました。
HTTPリクエストが正常に動作したこともPostman、CloudTrailから確認できています。
次は生成されたHTTPリクエストの詳細を確認していきます。

生成されたHTTPリクエストの構成

Postmanの実行例を元に説明していきます。
HTTPリクエストの構成は以下になります。

HTTPリクエストの構成

生成されたHTTPリクエストの詳細

ここではHTTPリクエストの詳細を確認します。

リクエスト

POST / HTTP/1.1
  • Price List APIは、「POST」で処理を受け取ります
  • リソースの指定はないので、URLはすべて「/」になります
  • クエリパラメータの指定もありません

ヘッダ

Host: api.pricing.us-east-1.amazonaws.com
  • Hostは、APIのエンドポイントです
    <サービス>.<リージョン>.amazonaws.com になります
X-Amz-Date: 20230113T080428Z
  • APIのクライアントでの実行日時をISO8601形式で記載します(UTCで計算)
    署名の計算でも使用します
    クライアントの時刻とAWSのサーバの時刻が5分離れていると、Signature expiredでエラーになります
    x-amz-dateは、小文字でもOKです
X-Amz-Target: AWSPriceListService.DescribeServices
  • APIのアクションのためにクエリパラメータに利用しないので、ヘッダにアクションを記載します
    <サービス>.<アクション>で記載するので、Price List APIの他のアクションは、X-Amz-Targetとリクエストボディを変更すれば実行できます
X-Amz-Content-Sha256:
  • リクエストボディ({})をSha256でハッシュ化したものです
    署名の計算でも使用します
    256bitなので、16進数表記で64文字です
Authorization:
  • 認証認可のための情報群です。以下で構成されています
    • AWS4-HMAC-SHA256
      - 認証方式のため、固定値です(後ろの文字列とはスペースで区切られる)
      AWS 署名バージョン 4(AWS4)と署名アルゴリズム(HMAC-SHA256)を指定します
    • Credential=AKIAIOSFODNN7EXAMPLE/20230113/us-east-1/pricing/aws4_request,
      - <アクセスキー>/<日付>/<リージョン>/<サービス>/aws4_request
      /区切りで指定します
      日付は YYYYMMDD形式でのクライアントでの実行日です(UTCです)
    • SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target,
      - 署名計算に使用したヘッダをリストします
      すべて小文字に変換し、昇順に並び替えて、;で連結します
    • Signature=0cae2bc25fc75c011f44cd9a4d4aaee0f19fd74e5545d5eb9c4b2e7e38081359
      - 署名の文字列です
      署名の計算で値を求めます
      64文字です
User-Agent: PostmanRuntime/7.30.0
  • 任意(署名では使っていません)
Postman-Token: c5ee81ab-897f-4de9-b4f9-4f883d44d30f
  • 任意(署名では使っていません)

ボディ

ボディ
  • リクエストが不正でないかの確認のため、ハッシュ化して、署名の計算で使用します

どのように指定するかは利用するサービスのAPIによって変わります。

署名の計算

Authorizationヘッダを作成するために必要な署名の計算(Signatureの算出)を詳細に見ていきます。署名の計算のためには、「リクエストを整形」、「署名対象の文字列を作成」、「署名用のキーを作成」、「署名を実施しリクエストの追加」を行います。

以下、順番に記載していきます。

  1. 正規リクエストの形式に変換
  2. 署名対象の文字列を作成
  3. 署名用のキーを作成
  4. SignatureV4の署名を計算し、HTTPリクエストにAuthorizationヘッダを追加

1.正規リクエストの形式に変換

実行例のHTTPリクエストを以下の形式に変換します。

# 正規リクエスト
canonical_request =
  http_request_method + '¥n' +
  canonical_uri + '¥n' +
  canonical_query_string + '¥n' +
  canonical_headers + '¥n' +
  signed_headers + '¥n' +
  HexEncode(Hash(request_payload))
http_request_method
  • POSTです
canonical_uri
  • /です
canonical_query_string
  • クエリパラメータは使用しないため、空行です
canonical_headers
  • ヘッダの名前と値を以下のルールに従って処理します
    ヘッダの名前はすべて小文字にする
    ヘッダの値は前後の空白を削除し、連続する空白を削除する
    ヘッダを昇順に並び替え、改行(¥n)で連結する
    最後に改行(¥n)を追加する
  • 以下のヘッダが対象です。
    content-type、host、x-amz-content-sha256、x-amz-date、x-amz-target
signed_headers
  • CanonicalHeadersの対象のヘッダ名を;で連結します(小文字かつ昇順)
HexEncode(Hash(request_payload))
  • request_payloadはボディ(今回は{})です
  • sha256でエンコードし、16進文字列にします
    X-Amz-Content-Sha256と同じです

結果として正規リクエストは以下になります。

# 正規リクエスト形式
POST
/
 
content-type:application/x-amz-json-1.1
host:api.pricing.us-east-1.amazonaws.com
x-amz-content-sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
x-amz-date:20230113T080428Z
x-amz-target:AWSPriceListService.DescribeServices
 
content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target
44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a

2.署名対象の文字列を作成

# 署名対象の文字列
string_to_sign =
    algorithm + ¥n +
    request_datetime + ¥n +
    credential_scope + ¥n +
    hashed_canonical_request
algorithm
  • AWS4かつHMAC-SHA256固定です
request_datetime
  • クライアントの実行日時をISO8601の形式で記載します(YYYYMMDD'T'HHMMSS'Z')
    X-Amz-Dateと一致します
credential_scope
  • 実行日時の日付部分(YYYYMMDD)、リージョン、サービス、固定値(aws4_request)を/で連結します
hashed_canonical_request
  • 1.で成形した正規リクエストをSHA256でハッシュします

署名対象の文字列は以下になります。

# 署名対象の文字列
AWS4-HMAC-SHA256
20230113T080428Z
20230113/us-east-1/pricing/aws4_request
c027d96ab87112b2fbf15d073f20e317a284fe734806ca62ae07cf154e9b5d6b

3.署名用のキーを作成

署名用のキーの疑似コードは以下になります(キーはバイナリになります)。だいたいはCredentialScopeと同じ値を使用します。

kSecret = <シークレットキー>
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")

HMACは(キー、データ)の順です。結果は256バイトです。

kSecret
  • IAMのシークレットキーです
kDate
  • キーは、固定値は「AWS4」+シークレットです
  • 対象データのDateは、YYYYMMDDです
    今回は20230113です
kRegion
  • 対象データはRegionなので、us-east-1です
kService
  • 対象データはServiceなので、pricingです
kSigning
  • 対象文字列の「aws4_request」は固定値です

4.SignatureV4の署名を計算し、HTTPリクエストにAuthorizationヘッダを追加

Authorizationヘッダを作成します。

# Authorizationヘッダ
Authorization: algorithm Credential=<アクセスキー>/credential_scope, 
SignedHeaders=signed_headers, Signature=signature
algorithm
  • 認証のタイプを指定します
    SignatureV4の場合は、AWS4-HMAC-SHA256です
    2.のalgorithmと同じです
Credential=<アクセスキー>/credential_scope,
  • IAMのアクセスキーとcredential_scopeを/で連結します
  • credential_scopeは、2.のcredential_scopeと同じです
SignedHeaders=signed_headers,
  • signed_headersは、1.のsigned_headersと同じです
Signature=signature
  • 3.の署名用のキー、2.の対象文字列からHMAC-SHA256を計算します。

Authorizationヘッダは以下になります。

# Authorizationヘッダ
Authorization: 
AWS4-HMAC-SHA256 
Credential=AKIAIOSFODNN7EXAMPLE/20230113/us-east-1/pricing/aws4_request, 
SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target, 
Signature=0cae2bc25fc75c011f44cd9a4d4aaee0f19fd74e5545d5eb9c4b2e7e38081359

以上がHTTPリクエストの詳細となります。

最後に

今回は、AWS APIを実行する時に使用されるHTTPリクエストの中身を見ていきました。APIの署名の計算にリージョン名が使用されていることから、リージョン毎にAPIを受け付けていることが分かります。AWS APIを実行するHTTPリクエストを生成することができれば、「AWS サービスを管理するための統合ツール:AWS CLI」や「ソフトウェア開発キット(SDK:Software Development Kit)」が何らかの制限で使用できない状況になってもAWSのサービスを実行できるようになります。

AWSの理解を深めたいと考えている方の参考になれば幸いです。

この記事は役に立ちましたか?

はい いいえ
関連サービス
AWSインテグレーション