ようへいの日々精進XP

よかろうもん

無料枠でざっくり学ぶ Microsoft Azure その 2 ~ Azure Blob ストレージの SAS(共有アクセス署名) について調べたメモ ~

tl;dr

Amazon S3 で言うところの Presigned URL 的なものが Azure Blob ストレージにもきっとあるだろうと思って調べたらあったのでメモ。

azure.microsoft.com

その名も Shared Access Signature(共有アクセス署名)という名前。Southern All Stars や Scandinavian Airlines System や Serial Attached SCSI 等、我々の周りにはいくつかの SAS が存在しているが、今回、新しい SAS が加わった形となる。


参考


共有アクセス署名とは(ドキュメントをざっくりと纏める)

ざっくり

  • Blob ストレージ内のオブジェクトに対して期間とアクセス許可を指定してクライアントにアクセスさせる為の仕組み
  • クライアントに対してストレージアクセスキーを付与することなく比較的安全にオブジェクトにアクセスさせることが出来る
  • URL クエリパラメータ内にストレージリソースへの認証アクセスに必要な情報が含まれている

共有アクセス署名の機能(URI

共有アクセス署名で利用される URI には下図のようなパラメータが含まれる。(オレンジ色は必須項目)

https://msdn.microsoft.com/dynimg/IC746703.jpeg

(出典:https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx

  • ストレージリソース
  • コンテナーと Blob
  • ファイル共有とファイル
  • キュー
  • テーブルとテーブルエンティティの範囲
  • 開始時刻(SAS が有効になる時刻)
  • 有効期限(SAS が無効になる時刻)
  • アクセス許可

以下は URL の例。

https://myaccount.blob.core.windows.net/sascontainer/sasblob.txt?sv=2012-02-12&st=2013-04-29T22%3A18%3A26Z&se=2013-04-30T02%3A23%3A26Z&sr=b&sp=rw&sig=Z%2FRHIX5Xcg0Mq2rqI3OlWTjEg2tYkboXr1P9ZUXDtkk%3D

以下は URL パラメータの詳細。

名前 リンク / セクション 説明
Blob URI https://myaccount.blob.core.windows.net/sascontainer/sasblob.txt BLOB のアドレス。HTTPS の使用を推奨。
ストレージサービスのバージョン sv=2012-02-12 ストレージ サービス バージョン 2012-02-12 以降では、このパラメーターは、使用するバージョンを示す。
開始時刻 st=2013-04-29T22%3A18%3A26Z ISO 8061 形式で指定。SAS をすぐに有効にする場合は、開始時刻を省略する。
有効期限 se=2013-04-30T02%3A23%3A26Z ISO 8061 形式で指定。
リソース sr=b リソースは BLOB を指定。
アクセス許可 sp=rw SAS で付与されるアクセス許可には、読み取り (r) および書き込み (w) が含まれる。
署名 sig=Z%2FRHIX5Xcg0Mq2rqI3OlWTjEg2tYkboXr1P9ZUXDtkk%3D BLOB へのアクセスを認証するために使用する。署名は、SHA256 アルゴリズムを使用して署名対象文字列とキーを計算した後に、Base 64 エンコードを使用してエンコードした HMAC 値。

(出典:https://azure.microsoft.com/ja-jp/documentation/articles/storage-dotnet-shared-access-signature-part-1/#-3

SAS の種類

以下の形式の SAS が利用可能。

以下に整理。

形式 詳細
アドホック SAS 開始時刻、有効期限、アクセス許可が SAS URL で指定される(コンテナ、Blob、ファイル共有、ファイル、テーブル、キューで作成可能)
アクセスポリシー + SAS コンテナに付与するアクセスポリシー(プライベート/パブリックコンテナー/パブリック Blob)と SAS の併用

アクセスポリシーについては以下のドキュメントにて。

azure.microsoft.com


Azure SDK for Ruby による SAS の実装

せっかくなので...

Azure SDK for Ruby(0.6.4) を利用して Blob オブジェクトの SAS URI を作成してみる。

参考

共有アクセス署名の生成

ドキュメントの共有アクセス署名 URI の構築に詳細に記載されている(※「署名の指定」及び「署名文字列の作成」セクション)。ストレージのバージョン毎に署名に利用する対象の文字列が異なるので注意が必要。また、署名の生成に際しては署名対象文字列以外にストレージアカウントキーが必要となる。

尚、署名は以下のステップで生成する。

  1. 署名対象文字列を連結して UTF-8エンコード
  2. 1 で生成した文字列ととストレージアカウントキーを SHA256 で計算
  3. 2 で計算した値を HMAC でハッシュ化して Base64 エンコード

上記のステップはドキュメントとサンプルソースをななめ読みした個人的な解釈なので誤りがあるかもしれない。

尚、以下のサンプルに関しては「2012/02/12 よりも以前のバージョン」を参考に以下の署名対象文字列を利用した。

StringToSign = signedpermissions + "\n"
               signedstart + "\n"
               signedexpiry + "\n"
               canonicalizedresource + "\n"
               signedidentifier

各文字列については以下のとおり。(ドキュメントより一部抜粋)

フィールド名 クエリパラメーター 説明
signedstart st 省略可能。共有アクセス署名が有効になる時刻 (ISO 8601 形式)。省略した場合には現在時刻となる。2012-02-12 より前のバージョンではコンテナー ポリシーを使用する場合を除き signedstart から signedexpiry までの期間が 1 時間を超えた指定は出来ない。
signedexpiry se 必須。共有アクセス署名が無効になる時刻 (ISO 8601 形式)。
signedpermissions sp 必須。共有アクセス署名と関連付けられているアクセス許可。BLOB の場合には「読み取り(r)と書込(w)と削除(d)」を指定することが出来る。
signedresource sr 必須。共有リソースが BLOB の場合は、b を指定する。共有リソースがコンテナーの場合は、c を指定する。
canonicalizedresource 署名対象リソースへの正規化されたパス。この部分にはストレージ アカウント名とリソース名が含まれている必要がある。

サンプル

上記の参考記事を参考に以下のようなサンプルをこさえた。

github.com

Dockerfile もあるので docker run で試す。(事前にストレージアカウントとコンテナを作成しておく)

#
# SAS URI 発行
#

$ docker run \
> --env 'STORAGE_ACCOUNT_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
> --env 'STORAGE_ACCOUNT_NAME=komanechi' \
> --env 'STORAGE_CONTAINER_NAME=foo' \
> --env 'EXPIRE_TIME=30' \
> --env 'CONTENT_KEY=foo' \
> --env 'CONTENT=bar' \
> inokappa/azure-storage-sas-ruby
SAS URI              : https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z
Start Time           : 2015-09-10T01:01:31Z
End Time             : 2015-09-10T01:02:01Z
Response Status Code : 200
Response Body        : bar

#
# 確認
#

# アクセス OK
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
bar

# 30 秒後...アクセス不可
$ curl "https://komanechi.blob.core.windows.net/foo/foo?se=2015-09-10T01%3A02%3A01Z&sig=sNl%2BAq8lOOqspapwcYVGrELZTaUBRTxNN3QIITc6DEY%3D&sp=r&sr=b&st=2015-09-10T01%3A01%3A31Z"
・ソ<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signa ture.
RequestId:da998a44-0001-0032-5464-ebccc4000000
Time:2015-09-10T01:02:01.4635058Z</Message><AuthenticationErrorDetail>Signature not valid in the specified time frame: Start [Thu, 10 Sep 2015 01:01:31 GMT] - Expiry [Thu, 10 Sep 2015 01:02:01 GMT] - Current [Thu, 10 Sep 2015 01:02:01 GMT]</AuthenticationErrorDetail></Error>

まとまってないまとめ

共有アクセス署名とは

  • Amazon S3 の Presigend URL のようにアクセス権限と期限を設けた一時的な URL を発行する際に利用する
  • 署名は対象の文字列(ストレージバージョンによって異なる)をハッシュ化等して生成する
  • 共有アクセス署名 URI には署名に合わせて、対象となるリソース、期限や権限をパラメータとして定義する
  • 各ストレージ(Blob / テーブル / キュー)に指定することが出来る
  • Azure SDK for Ruby の 0.6.4 だと署名の生成、URL の生成は独自に実装する必要がありそう(最新の 0.7.0 だと実装されているようだけどどのように使うのか不明...すいません)

その他の言語での実装例

PythonPHP での実装例。