Imagine your application lets users upload profile pictures or download invoices from Azure Blob Storage. You want users to access their files without making the entire storage account public or sharing your storage account key.
Azure Shared Access Signature (SAS) tokens can help. A SAS token gives limited access to a storage resource. You can control which operations are allowed, how long the access lasts, and which resource the token applies to.
For example, you can create a link that allows a user to download one invoice for the next 10 minutes. After the token expires, the link can no longer be used for that access.
However, creating a SAS token with the wrong permissions or an unnecessarily long expiry can expose your data. In this article, you will learn how SAS tokens work, how to create them in C#, and how to limit access safely.
What Is an Azure SAS Token?
A Shared Access Signature is a signed set of access rules attached to an Azure Storage request. It allows a client to access a resource without receiving your storage account key.
Depending on the SAS type and configuration, you can control:
- Resource: Which blob, container, or other supported storage resource can be accessed.
- Permissions: Whether the client can read, write, delete, or perform other allowed operations.
- Expiry: When the token stops being valid.
- Start time: When the token becomes valid, if a start time is specified.
- Network restrictions: Whether access is limited to supported IP ranges or protocols.
A SAS token is usually added to a storage URL as a query string.
For example, a URL might contain parameters that specify the permitted operation and expiry time. Anyone who obtains a usable SAS URL may be able to perform the operations allowed by that token.
Treat a SAS URL like a temporary password. Do not place it in public logs, expose it unnecessarily, or send it to people who should not have access.
Understand the Different Types of SAS
Azure Storage supports different SAS types. Choosing the right one matters because the available permissions and restrictions depend on the type.
Service SAS
A Service SAS grants access to a specific storage service resource, such as a blob or container.
It is useful when an application needs to give a user limited access to a particular file.
A Service SAS can be signed using an account key or, for supported scenarios, a user delegation key.
Account SAS
An Account SAS can grant access across supported services and resource types within a storage account.
Because it can cover a broader scope, it needs careful permission planning. Avoid using an Account SAS when a narrower token is enough.
User Delegation SAS
A User Delegation SAS is signed using a user delegation key obtained through Microsoft Entra ID authentication.
It is often a good choice for applications that need to generate temporary access to Blob Storage without relying on the storage account key.
The application identity needs the appropriate permissions to obtain the delegation key and access the relevant resources.
For many applications that generate temporary links to blobs, a User Delegation SAS is a sensible starting point.
Step 1: Install the Azure Storage SDK
For a .NET application, install the Azure Blob Storage SDK:
dotnet add package Azure.Storage.Blobs
You will also need the Azure Identity package if your application uses Microsoft Entra ID authentication:
dotnet add package Azure.Identity
These packages provide the classes needed to connect to Blob Storage, authenticate, and create supported SAS tokens.
Step 2: Connect to Blob Storage Using Microsoft Entra ID
Avoid putting storage account keys directly into source code.
For applications running in Azure, managed identity is often a good way to authenticate without storing credentials in application settings. For local development, you can use an appropriate developer identity through the Azure Identity library.
Here is an example:
using Azure.Identity;
using Azure.Storage.Blobs;
string accountUrl =
"https://YOUR_STORAGE_ACCOUNT.blob.core.windows.net";
var blobServiceClient = new BlobServiceClient(
new Uri(accountUrl),
new DefaultAzureCredential());
var containerClient =
blobServiceClient.GetBlobContainerClient("invoices");
var blobClient =
containerClient.GetBlobClient("invoice-1001.pdf");
Replace the storage account name and container name with your own values.
DefaultAzureCredential tries supported authentication methods available in the current environment. In production, configure the application's identity deliberately and grant it only the permissions it needs.
For User Delegation SAS generation, the identity must also be allowed to obtain a user delegation key. Having permission to read a blob does not automatically mean the identity can generate every type of SAS token.
Step 3: Create a SAS Token with a Short Expiry
The following example creates a read-only User Delegation SAS for a single blob.
The token will be valid for a short period, allowing a user to download the file without granting access to the entire container.
using Azure.Storage;
using Azure.Storage.Blobs;
using Azure.Storage.Sas;
DateTimeOffset now = DateTimeOffset.UtcNow;
UserDelegationKey delegationKey =
await blobServiceClient.GetUserDelegationKeyAsync(
now.AddMinutes(-5),
now.AddHours(1));
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerClient.Name,
BlobName = blobClient.Name,
Resource = "b",
StartsOn = now.AddMinutes(-5),
ExpiresOn = now.AddMinutes(10)
};
sasBuilder.SetPermissions(BlobSasPermissions.Read);
var sasUriBuilder = new BlobUriBuilder(blobClient.Uri)
{
Sas = sasBuilder.ToSasQueryParameters(
delegationKey,
blobServiceClient.AccountName)
};
Uri sasUri = sasUriBuilder.ToUri();
Console.WriteLine(sasUri);
This example assumes that blobServiceClient, containerClient, and blobClient have already been created as shown in the previous step.
Let's look at the important parts.
- Resource = "b" identifies the target as a blob.
- SetPermissions(BlobSasPermissions.Read) allows read access without granting write or delete access.
- StartsOn sets a start time slightly before the current time to reduce problems caused by small clock differences.
- ExpiresOn sets the token's expiry to 10 minutes after the current time.
- ToSasQueryParameters signs the SAS parameters using the delegation key.
The delegation key in this example is requested for one hour, while the SAS token itself expires after 10 minutes. These are different limits: the SAS must also remain within the delegation key's validity period.
The exact permitted lifetime and available operations depend on Azure Storage's rules and your configuration. Make sure the delegation key's validity covers the SAS interval you request.
Also, a short expiry does not make a token harmless if it is leaked. It limits the period during which the token can be used, but access remains available until the token expires or another applicable restriction prevents it.
Step 4: Give Users Only the Permissions They Need
A SAS token should grant the smallest set of permissions required for the task.
For example, if a user only needs to download an invoice, grant read access:
sasBuilder.SetPermissions(BlobSasPermissions.Read);
If a user needs to upload a file, the required permissions will be different. Do not grant read, write, and delete access just because it is easier.
For uploads, also consider whether the application should control the blob name. Allowing a client to write to arbitrary blob names can lead to unwanted overwrites or files being placed in the wrong location.
A safer design is to create a unique blob name on the server and generate a SAS token for that specific blob.
Step 5: Restrict the Token to the Correct Resource
A token for one blob is generally safer than a token for an entire container when the user only needs one file.
In the earlier example, the SAS builder specifies both the container and the blob name:
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerClient.Name,
BlobName = blobClient.Name,
Resource = "b",
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(10)
};
The Resource = "b" setting indicates a blob-level resource.
Avoid generating a container-level SAS unless the client genuinely needs access to multiple blobs. A broader scope increases the number of resources that could be affected if the token is shared or stolen.
You should also check whether the target blob belongs to the user requesting access. Generating a valid SAS token for the wrong user's file is still an authorization failure.
Step 6: Understand Expiry and Revocation
Expiry controls how long the SAS token can be used. It does not automatically guarantee immediate revocation when a user's account is disabled or their permissions change.
For example, a user might receive a SAS URL that expires in one hour. If the user signs out five minutes later, the SAS URL may still work until it expires.
This is why you should choose a short, practical lifetime.
Depending on the SAS type, revocation options differ. User Delegation SAS tokens can be affected by revoking user delegation keys, but this can affect multiple tokens associated with the relevant key. Service SAS tokens signed with an account key have different revocation considerations, including the use of stored access policies where supported.
Do not assume that deleting a blob or changing an application's user permissions will always invalidate every previously issued SAS URL in the way you expect. Test the revocation behavior for your chosen SAS type.
Common Mistakes to Avoid
| Mistake |
Why it is risky |
Better approach |
| Setting a very long expiry |
A leaked URL remains usable for longer |
Use the shortest practical lifetime |
| Granting unnecessary permissions |
A token may allow unwanted changes or deletion |
Grant only the required permissions |
| Sharing a container-level token for one file |
The token may cover more resources than needed |
Prefer a blob-level SAS |
| Logging full SAS URLs |
Logs may expose usable access tokens |
Redact SAS query parameters |
| Using storage account keys in application code |
Credentials can be leaked through source code or configuration |
Prefer Microsoft Entra ID and managed identity where suitable |
| Assuming expiry provides instant revocation |
A token may remain usable until its validity ends |
Plan for revocation and test the chosen SAS type |
| Skipping ownership checks |
A valid token can still expose another user's file |
Authorize the user before generating the SAS |
Best Practices for Production Applications
Follow these practices when generating SAS tokens:
- Keep expiry short. Match the lifetime to the expected download or upload operation.
- Limit permissions. Grant only the operations required by the client.
- Limit the resource scope. Prefer access to one blob when one file is all the user needs.
- Use Microsoft Entra ID where appropriate. Managed identity and User Delegation SAS can reduce reliance on storage account keys.
- Authorize before generating a token. Confirm that the signed-in user is allowed to access the requested file.
- Protect SAS URLs. Avoid placing them in public logs, analytics events, or long-lived client storage.
- Plan for revocation. Understand how your SAS type behaves when access needs to be withdrawn.
- Monitor storage access. Use appropriate Azure Storage logging and monitoring to investigate unexpected activity.
Conclusion
Azure SAS tokens let you give users temporary access to storage resources without sharing your storage account key. You can control the resource, permissions, and expiry time to reduce unnecessary access.
For many applications, a short-lived, read-only User Delegation SAS for a single blob is a good starting point. Use Microsoft Entra ID where appropriate, check the user's permissions before generating the token, and avoid exposing SAS URLs in logs or public locations.
Remember that a SAS URL is a bearer credential: anyone who obtains it may be able to use the access it grants. Keep the scope narrow, set a suitable expiry, and understand the revocation options before relying on SAS tokens for sensitive files.
Join the conversation! Your thoughts help the community grow.