Azure Blob Storage と JavaScript を使用してユーザー委任 SAS トークンを作成する

この記事では、JavaScript 用の Azure Blob Storage クライアント ライブラリ v12 でユーザー委任 SAS トークンを作成する方法について説明します。 バージョン 2018-11-09 で導入されたユーザー委任 SAS は、Azure AD 資格情報によってセキュリティで保護され、Blob service では次の場合にのみサポートされます。

  • 既存のコンテナーへのアクセス権を付与します。
  • BLOB を作成、使用、削除するためのアクセス権を付与します。

ユーザー委任 SAS を作成するには、クライアントに blobServiceClient.getUserDelegationKey 操作を呼び出すアクセス許可が必要です。 この操作によって返されるキーは、ユーザー委任 SAS に署名するために使用されます。 この操作を呼び出すセキュリティ プリンシパルには、Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action を含む RBAC ロールを割り当てる必要があります。

SAS を所有するクライアントに付与されるアクセス許可は、ユーザー委任キーを要求したセキュリティ プリンシパルに付与されたアクセス許可と、署名されたアクセス許可 (sp) フィールドの SAS トークンのリソースに付与されたアクセス許可の共通部分です。 RBAC を介してセキュリティ プリンシパルに付与されたアクセス許可も SAS トークンに付与されていない場合、そのアクセス許可は、SAS を使用してリソースにアクセスしようとするクライアントには付与されません。

実行可能な Node.js ファイルとして GitHub からサンプル コード スニペットを入手できます。

パッケージ (npm) | サンプル | API リファレンス | ライブラリ ソース コード | フィードバックを送る

ユーザー委任 SAS トークンのベスト プラクティス

SAS トークンを持つすべてのユーザーが、それを利用してコンテナーや BLOB にアクセスできるため、最も制限の厳しいアクセス許可で SAS トークンを定義した上で、トークンが必要なタスクを完了できるようにする必要があります。

SAS トークンに関するベスト プラクティス

Azure Cloud で DefaultAzureCredential を使用する

シークレットなしで Azure に対して認証を行うには、マネージド ID を設定します。 これにより、コードで DefaultAzureCredential を使用できます。

Azure クラウドのマネージド ID を設定するには、次のようにします。

  • マネージド ID の作成
  • ID に適切なストレージ ロールを設定する
  • マネージド ID と連携するように Azure 環境を構成する

これら 2 つのタスクが完了したら、接続文字列またはアカウント キーの代わりに DefaultAzureCredential を使用します。 これにより、すべての環境でまったく同じソース コードを使用できるようになり、ソース コードでシークレットを使用するという問題がなくなります。

ローカル開発で DefaultAzureCredential を使用する

ローカル開発環境では、ローカル ランタイムとクラウド ランタイムで同じコードを使用するために、Azure ID (Azure portal へのサインインに使用する個人または開発アカウント) を Azure に対して認証する必要があります。

コンテナー: 必要な依存関係をアプリケーションに追加する

コンテナー SAS トークンを作成するために必要な依存関係を含めます。

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    ContainerClient,
    BlobServiceClient,
    ContainerSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

コンテナー: 環境変数を取得する

Blob Storage アカウント名とコンテナー名は、コンテナー SAS トークンを作成するために最低限必要な値です。

// Get environment variables for DefaultAzureCredential
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

DefaultAzureCredential を使用して SAS を作成する

DefaultAzureCredential を使用して SAS トークンを作成するには、次の概念的な手順が必要です。

  • DefaultAzureCredential を設定する
    • ローカル開発 - 個人 ID を使用し、ストレージのロールを設定する
    • Azure クラウド - マネージド ID を作成する
  • DefaultAzureCredential を使用して UserDelegationKey でユーザー委任キーを取得する
  • ユーザー委任キーを使用して、generateBlobSASQueryParameters で適切なフィールドを持つ SAS トークンを作成する

コンテナー: DefaultAzureCredential を使用して SAS トークンを作成する

ID が構成されている場合は、次のコードを使用して、既存のアカウントとコンテナー用のユーザー委任 SAS トークンを作成します。

// Server creates User Delegation SAS Token for container
async function createContainerSas() {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only list permission to list blobs 
    const containerPermissionsForAnonymousUser = "l";

    // Best practice: SAS options are time-limited
    const sasOptions = {
        containerName,                                           
        permissions: ContainerSASPermissions.parse(containerPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

上記のサーバー コードは、コンテナー SAS トークンを作成するために値のフローを作成します。

コンテナー SAS トークンを作成したら、トークンを使用するクライアントにそれを提供できます。 その後、クライアントはそれを使用してコンテナー内の BLOB を一覧表示できます。 クライアント コード例では、SAS をコンシューマーとしてテストする方法を示しています。

コンテナー: SAS トークンを使用する

コンテナー SAS トークンが作成されたら、そのトークンを使用します。 SAS トークンを使用する例として、次の手順を実行します。

  • コンテナー名とクエリ文字列を含む完全な URL を作成します。 クエリ文字列は SAS トークンです。
  • コンテナー URL を使用して ContainerClient を作成します。
  • クライアントを使用します。この例では、listBlobsFlat を使用してコンテナー内の BLOB を一覧表示します。
// Client or another process uses SAS token to use container
async function listBlobs(sasToken){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;
    
    // Create Url
    // SAS token is the query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}?${sasToken}`;
    console.log(`\nContainerUrl = ${sasUrl}\n`);

    // Create container client from SAS token url
    const containerClient = new ContainerClient(sasUrl);

    let i = 1;

    // List blobs in container
    for await (const blob of containerClient.listBlobsFlat()) {
        console.log(`Blob ${i++}: ${blob.name}`);
    }    
}

BLOB: 必要な依存関係をアプリケーションに追加する

BLOB SAS トークンを作成するために必要な依存関係を含めます。

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    BlockBlobClient,
    BlobServiceClient,
    BlobSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

BLOB: 環境変数を取得する

Blob Storage アカウント名とコンテナー名は、BLOB SAS トークンを作成するために最低限必要な値です。

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

BLOB SAS トークンを作成する必要がある場合は、SAS トークンを作成するための BLOB 名が必要です。 これは、ランダムな BLOB 名、ユーザーが送信した BLOB 名、アプリケーションから生成された名前など、事前に定義されます。

// Create random blob name for text file
const blobName = `${(0|Math.random()*9e6).toString(36)}.txt`;

BLOB: DefaultAzureCredential を使用して SAS トークンを作成する

ID が構成されている場合は、次のコードを使用して、既存のアカウントとコンテナー用のユーザー委任 SAS トークンを作成します。

// Server creates User Delegation SAS Token for blob
async function createBlobSas(blobName) {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only create/write permission to upload file
    const blobPermissionsForAnonymousUser = "cw"

    // Best practice: SAS options are time-limited
    const sasOptions = {
        blobName,
        containerName,                                           
        permissions: BlobSASPermissions.parse(blobPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

上記のコードは、コンテナー SAS トークンを作成するために値のフローを作成します。

BLOB SAS トークンを作成したら、トークンを使用するクライアントにそれを提供できます。 その後、クライアントはそれを使用して BLOB をアップロードできます。 クライアント コード例では、SAS をコンシューマーとしてテストする方法を示しています。

BLOB: SAS トークンを使用する

BLOB SAS トークンが作成されたら、そのトークンを使用します。 SAS トークンを使用する例として、次の手順を実行します。

  • コンテナー名、BLOB 名、クエリ文字列を含む完全な URL を作成します。 クエリ文字列は SAS トークンです。
  • コンテナー URL を使用して BlockBlobClient を作成します。
  • クライアントの使用: この例では、upload で BLOB をアップロードします。
// Client or another process uses SAS token to upload content to blob
async function uploadStringToBlob(blobName, sasToken, textAsString){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Create Url SAS token as query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}?${sasToken}`;
    console.log(`\nBlobUrl = ${sasUrl}\n`);

    // Create blob client from SAS token url
    const blockBlobClient = new BlockBlobClient(sasUrl);

    // Upload string
    await blockBlobClient.upload(textAsString, textAsString.length, undefined);    
}

関連項目