Best Practices for Accessing Azure Blob Storage with SAS Tokens
December 23, 2024

Best Practices for Accessing Azure Blob Storage with SAS Tokens

When anonymous access to Azure Blob storage is disabled, generating a Shared Access Signature (SAS) token is critical to securely accessing Blob files. SAS tokens allow you to grant temporary access to a blob file with defined permissions. In this article, I’ll introduce how to effectively generate and use SAS tokens in the ASP.NET Core API, and discuss the challenges and solutions for managing blob-level and container-level tokens.

Generating SAS tokens in ASP.NET Core
To handle operations related to Azure Blob storage in the ASP.NET Core API, I created a dedicated service class. This class manages SAS token generation and provides a URL containing the SAS token to retrieve the blob archive. Front-end applications can use these URLs to seamlessly access files.
Here’s how SAS token generation works at the blob level:

  1. The API generates a unique SAS token for each blob.
  2. The token is appended to the blob’s URL and returned to the client.
  3. Client applications use URLs to retrieve and display files.

Code to generate SAS tokens at the blob level

public Uri GetBlobSasUrl(string blobName)
{
    var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
    var blobClient = containerClient.GetBlobClient(blobName);

    if (blobClient.CanGenerateSasUri)
    {
        var sasBuilder = new BlobSasBuilder
        {
            BlobContainerName = _containerName,
            BlobName = blobName,
            Resource = "b",   // "b" for blob-level SAS
            ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(30),
        };

        sasBuilder.SetPermissions(BlobContainerSasPermissions.Read);

        Uri sasUri = blobClient.GenerateSasUri(sasBuilder);
        return sasUri;
    }
    else
    {
        throw new InvalidOperationException("BlobClient is not authorized to generate SAS tokens.");
    }
}

Enter full screen mode

Exit full screen mode

In this code resource=”b”, b is for blob “c” For container level.
While this approach works, it poses some challenges, especially in scenarios involving multiple users or systems that need to access many blobs. Below, I outline the main disadvantages of generating SAS tokens at the blob level.

Disadvantages of Blob Level SAS Tokens
1. Increase token generation overhead
• A SAS token needs to be generated for each individual blob.
• Frequent access to multiple blobs or batch operations in a short period of time may cause performance bottlenecks.
• This process adds computational overhead, slowing down applications with high throughput requirements.

2. Complexity of Token Management
• Generating and maintaining multiple SAS tokens for individual blobs complicates token management.
• Revoking or rotating tokens becomes more challenging because each blob’s token must be processed independently.

3. Reduced efficiency of large-scale access
• Blob-level SAS tokens restrict access to a single blob, making it difficult to perform batch operations such as enumerating blobs or downloading multiple archives using a single token.
• Generating multiple tokens to access multiple blobs reduces efficiency.

4. The risk of token expiration is high
• Blob-level SAS tokens are prone to expiration issues if not used within their validity period.
• Frequent token updates increase the risk of errors and complicate application workflows.

5. Not suitable for shared or dynamic workflows
• For shared or dynamic workflows, blob-level tokens require a separate token to be generated and distributed to each consumer.
• This method can become cumbersome when blobs are frequently added or deleted.

Transitioning to container-level SAS tokens
To address these challenges, I switched to container-level SAS tokens. This approach provides greater flexibility and efficiency in managing access to blob storage. Here’s why container-level tokens are better:

1. Simplify access management
o A single token grants access to all blobs in the container.
o Revoking or rotating tokens is simpler because only one token needs to be managed for the container.

2. Improve performance
o Tokens can be cached, reducing the computational overhead of generating tokens for each request.
o Batch operations such as listing or downloading multiple blobs are more efficient.

3. Flexibility of dynamic workflows
o Users and systems can access newly added blobs without generating additional tokens.
o Suitable for shared workflows where multiple users need to access the same container.

Code to generate SAS tokens at the container level

public Uri GetContainerSasUrl()
{
    var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);

    if (containerClient.CanGenerateSasUri)
    {
        var sasBuilder = new BlobSasBuilder
        {
            BlobContainerName = _containerName,
            Resource = "c",       // "c" for container-level SAS
            ExpiresOn = DateTimeOffset.UtcNow.AddHours(12), // Adjust expiration as needed
        };
        // Set container-level permissions
        sasBuilder.SetPermissions(BlobContainerSasPermissions.Read);

        Uri sasUri = containerClient.GenerateSasUri(sasBuilder);
        return sasUri;
    }
    else
    {
        throw new InvalidOperationException("ContainerClient is not authorized to generate SAS tokens.");
    }
}

Enter full screen mode

Exit full screen mode

cache token

public Uri GetCachedContainerSasUrl()
{
    if (!_cacheService.TryGet(Constants.CacheKeys.ContainerSasUrl, out Uri _cachedSasUri))
    {
        _cachedSasUri = GetContainerSasUrl(); 
        _cacheService.Set(Constants.CacheKeys.ContainerSasUrl, _cachedSasUri);
    }         
    return _cachedSasUri;
}

Enter full screen mode

Exit full screen mode

Execute the service from the API and prepare the final URL to be passed back

[HttpGet("getBlobUrl/{blobName}")]
public IActionResult GetBlobSasUrl(string blobName)
{
    var containerSasUrl = _blobStorageService.GetCachedContainerSasUrl();
    var blobUrl = $"{containerSasUrl.ToString().Split('?')[0]}/{blobName}{containerSasUrl.Query}";

    return Ok(blobUrl);
}

Enter full screen mode

Exit full screen mode

Advantages of using container-level SAS token implementation
By moving to container-level tokens, I achieved the following goals:
Enhanced performance: It reduces the latency of accessing files and minimizes the overhead of frequently generating tokens.
Scalability: Simplified workflow for applications that handle large numbers of blobs.
Easy to manage: Simplify token management and improve security by centrally controlling container-level access.


in conclusion

When using Azure Blob storage, choosing the right level to generate SAS tokens can significantly impact application performance and complexity. For scenarios involving frequent or high volume access, container-level SAS tokens provide a more efficient and easier-to-manage solution than blob-level tokens. By optimizing SAS token management, you can ensure secure, scalable, and efficient access to Azure Blob storage resources.

2024-12-23 14:57:24

Leave a Reply

Your email address will not be published. Required fields are marked *