yotiky Tech Blog

とあるエンジニアの備忘録

Azure Functions で Shared Access Signatures (SAS) を発行する

目次

検証環境

  • Azure Functions v3
  • Azure Storage Blobs v12.8.0

概要

Shared Access Signatures (SAS) は、リソースへのアクセス権に制限を付けてトークンを生成し、Shared Access Signatures URI を使ってリソースへのアクセスを許可する。

SASの種類は3種類。

  • ユーザー委任 SAS
  • サービス SAS
  • アカウント SAS

制御できる項目。

  • アクセスできるリソース
  • 書き込み、読み取りなどのアクセス許可
  • トークンの有効期限
  • IP制限

生成するには、ポータルや Microsoft Azure Storage Explorer を使う方法、プログラムで生成するなどがある。

ここではサービス SAS をプログラムで発行するサンプルとなる。 また、保存されているアクセス ポリシーにサービス SAS が関連付けられないため、強制的に失効させることができないので取り扱いは注意が必要。 有効期限を 1 時間以下に設定することが推奨されている。

docs.microsoft.com

実装

コンテナの SAS トーク

private static string _connectionString  = "connectionString";
private static string _container = "container";

[FunctionName("GetAccessToken")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log)
{
    var (url, token) = await GetContainerSasToken();
    log.LogInformation(url + token);

    return new OkObjectResult(token);
}

public static async Task<(string, string)> GetContainerSasToken()
{
    var account = CloudStorageAccount.Parse(_connectionString);
    var client = account.CreateCloudBlobClient();
    var container = client.GetContainerReference(_container);

    await container.CreateIfNotExistsAsync();

    var policy = new SharedAccessBlobPolicy()
    {
        SharedAccessStartTime = DateTime.UtcNow.AddMinutes(0),
        SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(3),
        Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.List,
    };

    var token = container.GetSharedAccessSignature(policy, null, null, null);

    // IP制限
    //var ipRange = new IPAddressOrRange("168.1.5.65");
    //var ipRange = new IPAddressOrRange("168.1.5.60", "168.1.5.70");
    //var token = container.GetSharedAccessSignature(policy, null, null, ipRange);

    return (container.Uri.AbsoluteUri, token);
}

ログに出力される URI:%3Aエンコードされているので : に戻すとそのままの URI でブラウザでアクセスできる。

Blob の SAS トーク

private static string _connectionString  = "connectionString";
private static string _container = "container";
private static string _blobName = "blobName";

[FunctionName("GetAccessToken")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    ILogger log)
{
    var (url, token) = await GetBlobSasToken();
    log.LogInformation(url + token);

    return new OkObjectResult(token);
}
public static async Task<(string, string)> GetBlobSasToken()
{
    var account = CloudStorageAccount.Parse(_connectionString);
    var client = account.CreateCloudBlobClient();
    var container = client.GetContainerReference(_container);

    await container.CreateIfNotExistsAsync();

    var blob = container.GetBlockBlobReference(_blobName);

    var exists = await blob.ExistsAsync();
    if (!exists) { return (null, null); }

    var policy = new SharedAccessBlobPolicy()
    {
        SharedAccessStartTime = DateTime.UtcNow.AddMinutes(0),
        SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(3),
        Permissions = SharedAccessBlobPermissions.Read,
    };

    var token = blob.GetSharedAccessSignature(policy, null, null, null, null);

    // IP制限
    //var ipRange = new IPAddressOrRange("168.1.5.65");
    //var ipRange = new IPAddressOrRange("168.1.5.60", "168.1.5.70");
    //var token = blob.GetSharedAccessSignature(policy, null, null, null, ipRange);

    return (blob.Uri.AbsoluteUri, token);
}

ログに出力される URI:%3Aエンコードされているので : に戻すとそのままの URI でブラウザでアクセスできる。

SAS トークンの生成と同時に、受け取ったパラメータに応じて Blob のパスをサーバー側で決めることもできるため、SAS URI 自体を返すようにするとクライアントは Blob や対象のファイルなど詳細を知らなくてよくなる。

参考

関連記事