AWS REST リクエストの署名についてメモ

AWS の REST API のリクエストでは署名(signature)を使って、API のセキュリティチェックを行っている。
サービス開始時に Version 1(いまは使われていない) が公開されたあと、2, 3, 4 という合計4種類が存在する。
この署名の仕様をメモ。

version 1

最も古い Version 1

?Action=CreateQueue
&QueueName=queue2
&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE
&SignatureVersion=1
&Expires=2007-01-12T12:00:00Z
&Version=2006-04-01

というような PARAM=VALUE がずらずらと並んだ GET リクエストを考える。

STEP1

パラメーターを大文字/小文字を無視して辞書順にソート。

?Action=CreateQueue
&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE
&Expires=2007-01-12T12:00:00Z
&QueueName=queue2
&SignatureVersion=1
&Version=2006-04-01

このリクエストから version1 で署名する文字列を以下のルールで生成。

クエリーストリングの先頭の ? を削除
クエリーストリングを大文字/小文字を無視して辞書順にソート
&name=value 形式の & と = を削除

※ name=value 形式の name と value は % エンコードしない

より具体的には

ActionCreateQueueAWSAccessKeyIdAKIAIOSFODNN7EXAMPLEExpires2007-01-12T12:00:00ZQueueNamequeue2SignatureVersion1Version2006-04-01

というようになっている。

この文字列を canonical string と呼ぶ。
この文字列を署名する。

STEP2

RFC2014 準拠の HMAC-SHA1 を求める。

  • HMAC のキーには AWS のシークレットアクセスキー
  • HMAC のメッセージには STEP2 で作成した文字列

STEP3

STEP2 で作成した文字列を BASE64 エンコードし、%エンコードしたものが署名。

urllib.quote_plus(hmac.new(secret_key, canonical_string, hashlib.SHA1).digest().encode('base64'))

この署名をクエリーストリングの Signature パラメーターで渡す。

この署名には大きな欠陥がある。
クエリーストリングのデリミターを利用していないため、 "foo=bar&fooble=baz""foo=barfooblebaz" は STEP2 の文字列生成で同じ文字列となり、当然、同じ署名となる。(ハッシュ値の衝突)

平文で API リクエストされると、クライアントが計算した署名を流用してリクエストパラメーターを改竄できてしまう。

ref : http://www.daemonology.net/blog/2008-12-18-AWS-signature-version-1-is-insecure.html

version 2

仕様

http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html

欠陥のある V1 を置き換えるために生まれたプロトコル。

STEP1

version2 で署名するメッセージは以下の形式をしている

HTTP_REQUEST_METHOD(GET or POST)
HOST
PATH
QUERY_STRING

より具体的には

GET\n
elasticmapreduce.amazonaws.com\n
/\n
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Action=DescribeJobFlows&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-10-03T15%3A19%3A30&Version=2009-03-31

というようになっている。

この文字列を canonical string と呼ぶ

  • HTTP_REQUEST_METHOD には GET または POST がくる
  • HOST は API のリクエスト先からポートを除外したもの
  • PATH は リクエストURL のパス部。デフォルトは /

QUERY_STRING は以下のルールで生成。

  • クエリーストリングの先頭の ? を削除
  • クエリーストリングを大文字/小文字を無視して辞書順にソート
  • name=value 形式の name と value は URL エンコード

STEP2

RFC2014 準拠の HMAC-SHA256 を求める。

  • HMAC のキーには AWS のシークレットアクセスキー
  • HMAC のメッセージには STEP1 で作成した文字列

STEP3

STEP2 で作成した文字列を BASE64 エンコードし、%エンコードしたものが署名。

urllib.quote_plus(hmac.new(secret_key, canonical_string, hashlib.SHA256).digest().encode('base64'))

この署名をクエリーストリングの Signature パラメーターで渡す。

最終的には以下のような URL となる

https://elasticmapreduce.amazonaws.com?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Action=DescribeJobFlows&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-10-03T15%3A19%3A30&Version=2009-03-31&Signature=i91nKc4PWAt0JJIdXwz9HxZCJDdiy6cf%2FMj6vPxyYIs%3D

version 3

signature version 3 は route53 だけで利用されている。

Amazon Route 53 is the only service that requires signature version 3.
http://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html

仕様

http://docs.aws.amazon.com/Route53/latest/APIReference/Headers-Common.html#Headers

Version 1,2 とことなり、署名ををリクエストヘッダーに突っ込む。

リファレンスに仕様を見つけられなかったので SDK の実装をもとに仕様を起こす

STEP1

GMT での現在日時を取得(Sat, 22 Nov 2014 11:02:12 GMT)
リクエストヘッダーの Date または x-amz-date に突っ込む

STEP2

RFC2014 準拠の HMAC-SHA256 を求める。

  • HMAC のキーには AWS のシークレットアクセスキー
  • HMAC のメッセージには STEP1 で作成した文字列

STEP3

STEP2 で作成した文字列を BASE64 エンコードし、%エンコードしたものが署名。

urllib.quote_plus(hmac.new(secret_key, gmt_datetime, hashlib.SHA256).digest().encode('base64'))

STEP4

リクエストヘッダーに Date または X-Amz-Date と X-Amzn-Authorization に Signature に続けて STEP3 で作成した署名を入れる。

X-Amz-Date : 20141122T135429Z
X-Amzn-Authorization : AWS3-HTTPS AWSAccessKeyId=YOUR-AWS-ACCESS-KEY,Algorithm=HmacSHA256,Signature=YOUR-SIGNATURE

version 4

最後に最新にして最も複雑な Version4 のシグネチャーについて。
AWS は version 4 への移行を推奨している。

AWS currently supports three signature versions: signature version 2, signature version 3, and signature version 4. This section covers signature version 4 and signature version 2. Most services support version 4, and if a service supports version 4, we strongly recommend that you use that version.
http://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html

事実 Frankfurt や Beijin のように最近誕生したリージョンでは V2 プロトコルに対応していない。

大きな違いとしては、 V1 から V3 までは HMAC の秘密鍵に AWS の生のシークレットキーを使っているのに対し、V4 では鍵導出関数(Key Derivation Function:KDF)を使っている。

仕様

http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

ややこしいからか、解説が異常に親切

STEP 1

リクエスト情報を含んだ canonical string を構成する。

HTTP_REQUEST_METHOD(GET or POST)
PATH
QUERY_STRING
CANONICAL_HEADER
SIGNED_HEADER
PAYLOAD

より具体的には

GET
/
Action=DescribeRegions&Version=2013-10-15
host:ec2.amazonaws.com
x-amz-date:20141122T135429Z

host;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

というようになっている。

  • HTTP_REQUEST_METHOD には GET または PUT がくる
  • PATH は リクエストURL のパス部。デフォルトは /
  • QUERY_STRING は Version 2 と同じ
  • CANONICAL_HEADER は今回はリクエストするホストとリクエスト日時
  • SIGNED_HEADER は hostx-amz-date を小文字で ; 区切りでアルファベット順に並べる。
  • PAYLOAD はリクエストボディー(GET なら空文字)の SHA256 ダイジェスト

STEP 2

version4 で署名するメッセージは以下の形式をしている

HASH_ALGORITHM
REQUEST_DATETIME
CREDENTIAL_SCOPE
CANONICAL_STRING

より具体的には

AWS4-HMAC-SHA256\n
20141122T135429Z\n
20141122/us-east-1/ec2/aws4_request\n
d8644052836bd7e18c938b2b1827a9599daed0bc5ad5f7e68cf87ddfa0153d47

というようになっている。

  • HTTP_REQUEST_METHOD にはハッシュ方式によって AWS4-HMAC-SHA256(推奨)または AWS4-HMAC-SHA1 がくる
  • REQUEST_DATETIME は GMT での現在日時
  • CREDENTIAL_SCOPE は {REQUEST_DATETIMEの年月日部分}/{aws-region}/{service-name}/aws4_request
  • CANONICAL_STRING には STEP1 で作成した文字列の SHA256 ダイジェスト

STEP3

鍵導出関数を使って署名に使うキーを生成する。

鍵は AWS の秘密鍵を初期値として HMAC-SHA256 を繰り返す。

kSecret = AWS_SECRET_KEY
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")

あるいは

HMAC(HMAC(HMAC(HMAC("AWS4" + AWS_SECRET_KEY, Date), Region), Service), "aws4_request")

と書いたほうがわかりやすいかもしれない。

STEP 4

RFC2014 準拠の HMAC-SHA256 を求める。

  • HMAC のキーには STEP 3 で作成した文字列
  • HMAC のメッセージには STEP2 で作成した文字列

これが署名

STEP 5

リクエストヘッダーに Date または X-Amz-DateX-Amzn-Authorization に アルゴリズム、クリデンシャル、署名ヘッダー、STEP3 で作成した署名を入れる。

Host : ec2.amazonaws.com
X-Amz-Date : 20141122T135429Z
Authorization : AWS4-HMAC-SHA256 Credential=YOUR-AWS-ACCESS-KEY/20141122/us-east-1/ec2/aws4_request, SignedHeaders=host;x-amz-date, Signature=YOUR-SIGNATURE

ここで Credential は {YOUR-AWS-ACCESS-KEY}/{REQUEST_DATETIMEの年月日部分}/{aws-region}/{service-name}/aws4_request で構成されている。(STEP 2 の CREDENTIAL_SCOPE に aws access key がついたもの)

署名情報はリクエストヘッダーではなくクエリーストリングや POST データに含めてもよい。

ref ; http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html

Advertisements
Tagged with: , , , , ,
Posted in algorithm, aws
One comment on “AWS REST リクエストの署名についてメモ
  1. yama says:

    非常に助かりました。

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 4 months ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 4 months ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 4 months ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 10 months ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 1 year ago
%d bloggers like this: