In today's digital landscape, data security and controlled access are critical concerns for businesses. Azure Blob Storage is a popular cloud storage solution, allowing you to store and manage unstructured data. To safeguard your data while granting controlled access to authorized users, one effective approach is to generate Shared Access Signature (SAS) tokens programmatically in .NET. In this blog post, we'll explore how to create SAS tokens in .NET, to enhance the security of your Azure Blob Storage.
All the code used in this blob post can be found in Github. Please check it out if you would like to have an example of this running locally in a Console app.
While Shared Access Signature (SAS) tokens provide a valuable mechanism for granting temporary, controlled access to specific resources in Azure Blob Storage, it is essential to acknowledge that SAS is not always the first option for security measures. In many scenarios, Role-Based Access Control (RBAC) using Azure Active Directory (Azure AD) or other identity providers should be the primary choice for access control and security enforcement.
SAS tokens are particularly useful in scenarios where you need to grant time-limited, fine-grained access to specific resources without granting broader permissions. For example, when collaborating with external users or granting access to temporary services, SAS tokens offer a secure approach for limited access without the need for permanent user accounts. Here are some additional use cases for using SAS if you are interested in learning more.
A SAS token is a secure, time-limited URL that grants limited access to specific resources in your Azure Blob Storage account. When you create a SAS token, you can define its permissions, validity period, and which resources can be accessed. This provides a powerful mechanism to grant temporary, controlled access to your Blob Storage without compromising your account's primary access keys.
When generating SAS tokens, adhering to the least privileged principle is crucial. Only provide the necessary permissions required for the specific task. Granting excessive permissions in the SAS token could expose your data to unnecessary risks. By defining precise permissions (read, write, list, delete, etc.) and setting an appropriate expiration time, you can ensure that the SAS token is valid only for the required duration and with limited access. If you are interested in learning more about SAS tokens, you can find more information in this document Storage SAS Overview (be sure to check out the Best Practices section which contains a few of the features we have and will discuss in this blog post).
Every SAS token is signed with a key. You can sign a SAS token with a user delegation key or with a storage account key (Shared Key).
A user delegation SAS token is signed with a user delegation key created using Azure AD credentials. Microsoft recommends that you use this approach when signing SAS tokens as it is more secure than using the account key. If it is not possible to use user delegation, then move to the Shared Key signing method. To learn more take a look at Create a user delegation SAS. We will cover this method in a future blog post.
A service or account SAS token is signed with the storage account key (Shared Key), granting access based on the SAS tokens' signing method. One thing to keep in mind with this approach is the rotation of these keys as a security best practice. Should your key get rotated after you have created a SAS token and before it has expired it will invalidate the SAS. To learn more take a look at Create a service SAS or Create an account SAS. This will be our focus of this blog post.
To generate the SAS tokens programmatically in .NET, you can utilize the Azure.Storage.Blobs library, a part of the Azure SDK for .NET. In this blog post we will focus on using the account key to sign the SAS tokens.
var blobServiceClient = new BlobServiceClient(connectionString);
var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
var blobClient = containerClient.GetBlobClient(blobName);
// set properties on BlobSasBuilder class
var sasBuilder = new BlobSasBuilder()
{
BlobContainerName = containerName,
//add IPRange using a string ip address
IPRange = new SasIPRange(IPAddress.Parse(startIPAddress), IPAddress.Parse(endIPAddress)),
StartsOn = DateTimeOffset.UtcNow,
ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(60), // Expiration time for the SAS token
};
// set the required permissions on the SAS token
sasBuilder.SetPermissions(BlobSasPermissions.Read | BlobSasPermissions.Write);
// add resource specific properties and generate the SAS
if (sasResourceType == SasResourceType.Container)
{
//Create token at the container level
sasBuilder.Resource = "c";
sasToken = containerClient.GenerateSasUri(sasBuilder);
}
else if (sasResourceType == SasResourceType.Blob)
{
//Create token at the blob level
sasBuilder.Resource = "b";
sasBuilder.BlobName = blobName;
sasToken = blobClient.GenerateSasUri(sasBuilder);
}
Console.WriteLine($"Generated SAS token: {sasToken}");
Now that we have created the SAS token, let's confirm it's working how we would expect. We will create a blob programmatically using the newly created SAS with a string for content. Let's look at the code.
static void TestSasToken(Uri uri, IConfiguration configuration)
{
Console.WriteLine("Testing the SAS token...");
try
{
var testFileName = configuration.GetSection("AzureStorageConfig")["BlobNameForTest"] ?? string.Empty;
var testContent = configuration.GetSection("AzureStorageConfig")["BlobContentAsString"] ?? string.Empty;
var containerName = configuration.GetSection("AzureStorageConfig")["ContainerName"] ?? string.Empty;
// Create BlobClient with the URI containing the SAS token
var blobServiceClient = new BlobServiceClient(uri, new BlobClientOptions { Transport = new HttpClientTransport() });
var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
var blobClient = blobContainerClient.GetBlobClient(testFileName);
var blobContent = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(testContent));
blobClient.Upload(blobContent);
}
catch (Exception ex)
{
Console.WriteLine($"Error while testing the SAS token: {ex.Message}");
}
Console.WriteLine("Completed testing the SAS token");
}
Again, the complete code for this blob post can be found in Github. Once you have followed the instructions in the README to get it running locally you will execute the following command to test creating a SAS token for a specific named blob. This will allow you to create the blob and read the blob.
dotnet run create-blob test
Here is the output:
And if we look in the container, we will see the bar.txt blob successfully created:
Now if you try to access another blob in that container (say test.json) with that same SAS token you will get the following:
Next, we will test creating the container SAS using the container command.
dotnet run create-container test
Here is the output:
When we combine the SAS token from our output with the file path, we can access any blob in the container. As a test, I am using `test.json` as my blob to request and its content is rendered in the browser as expected. Here is that:
Securing your Azure Blob Storage is crucial for protecting your valuable data. By creating SAS tokens programmatically in .NET using the account key as the backing mechanism, you can grant controlled and time-limited access to authorized users or applications without exposing your storage account's primary access keys. Stay tuned for our upcoming blog, where we'll delve into the enhanced security and access control offered by user-delegated keys for generating SAS tokens in Azure Blob Storage.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.