Azure Blob Storage 및 JavaScript를 사용하여 사용자 위임 SAS 토큰 만들기

이 문서에서는 JavaScript용 Azure Blob Storage 클라이언트 라이브러리 v12에서 사용자 위임 SAS 토큰을 만드는 방법을 보여 줍니다. 버전 2018-11-09에 도입된 사용자 위임 SAS는 Microsoft Entra 자격 증명으로 보호되며 Blob 서비스에 대해서만 지원됩니다.

  • 기존 컨테이너에 대해 액세스 권한을 부여합니다.
  • blob 만들기, 사용 및 삭제를 위한 액세스 권한을 부여합니다.

사용자 위임 SAS를 만들려면 클라이언트에 blobServiceClient.getUserDelegationKey 작업 호출 권한이 있어야 합니다. 이 작업으로 반환된 키는 사용자 위임 SAS를 서명하는 데 사용됩니다. 이 작업을 호출하는 보안 주체에는 Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action을 포함하는 RBAC 역할이 할당되어야 합니다.

SAS를 소유하는 클라이언트에 부여된 권한은 사용자 위임 키를 요청한 보안 주체에 부여된 권한 그리고 서명된 권한(sp) 필드에서 SAS 토큰의 리소스에 부여된 권한이 교차되는 부분입니다. RBAC를 통해 보안 주체에 부여된 권한이 SAS 토큰에 부여되지 않은 경우에는 이 권한이 SAS를 사용하여 리소스에 액세스하려고 시도하는 클라이언트에 부여되지 않습니다.

샘플 코드 조각은 GitHub에서 실행 가능한 Node.js 파일로 사용할 수 있습니다.

패키지(npm) | 샘플 | API 참조 | 라이브러리 소스 코드 | 제공 피드백

사용자 위임 SAS 토큰 모범 사례

SAS 토큰이 있는 사용자는 누구나 이를 사용해서 컨테이너 및 Blob에 액세스할 수 있으므로 토큰이 필요한 작업을 완료할 수 있도록 허용하는 가장 제한적인 권한으로 SAS 토큰을 정의해야 합니다.

SAS 토큰 모범 사례

Azure Cloud에서 DefaultAzureCredential 사용

비밀 없이 Azure에 인증하려면 관리 ID를 설정합니다. 이렇게 하면 코드에서 DefaultAzureCredential을 사용할 수 있습니다.

Azure Cloud에 대해 관리 ID를 설정하려면 다음을 수행합니다.

  • 관리 ID 만들기
  • ID에 적합한 스토리지 역할 설정
  • 관리 ID로 작동하도록 Azure 환경 구성

이러한 두 작업이 완료되면 연결 문자열 또는 계정 키 대신 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 Cloud - 관리 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: 애플리케이션에 필요한 종속성 추가

n 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);    
}

참고 항목