Delegate access to Azure Storage using Shared Access Signature (SAS)

Azure Storage allows many methods for authentication and authorization and it can be confusing when choosing the right one. For example, you can leverage Entra ID (formerly known as Azure Active Directory) and obtain a token for your user, service principal, or managed identity. Of course, methods that rely on Entra ID are generally preferred.

But sometimes using Entra ID is simply impossible or not practical. In such cases, you should probably consider using a Shared Access Signature. Although sometimes tools and SDKs make it easy to enter an account key and get going quickly, that's generally not a good idea and you should avoid using your storage account key. Shared Key authorization is another method not to be confused with Shared Access Signature. It has some resemblance to SAS as it needs to be signed with the account key though. But it's less secure than SAS and it isn't used so much nowadays.

Why is SAS needed?

Simply put, it's for delegation. But what is delegation one would ask and how is it different from trying to directly access a Storage Account to which we have an Azure RBAC role, for example?

Shared Access Signatures allow us to grant a client temporary access that is allowed to execute certain actions on a Storage Account. Those actions are restricted by the permissions you select such as Read, Write, List, etc. The SAS token is typically generated and passed to the client just before it needs to start using it. Then that client utilizes the SAS Token when invoking various Azure Storage APIs.

Here are a few examples that involve the blob service; however, keep in mind that SAS tokens can be also used for the Table, File, and Queue services of Azure Storage:

  • You want to enable users of your web application to download files stored in Azure Storage. When the user clicks on the link, the file is downloaded. Each link contains a SAS that is generated just before the file needs to be visualized in the UI, and the link expires in one hour.
  • A third-party system (that is not represented in Entra ID in any way) needs to download blobs from your storage account for some further processing.
  • Sometimes you want to overcome known issues with particular services. For example, I've had a case where we used an ADF pipeline to delete a directory in a very big data lake recursively. The linked service in ADF pointed to a managed identity that was granted some permissions via POSIX ACLs. During development, everything was fine and worked as expected but as the data lake grew, some recursive deletes started timing out. It turned out that ACLs were checked for every directory and file before anything could be deleted. Because the hierarchy under the path we wanted to delete was very deep, the recursive delete failed every time. This is indeed a limitation when you rely on ACLs to grant access. The only solution is to invoke the requests to Path - Delete as a super user so that ACLs are not checked at all. The solution for our issue was to generate a SAS with permissions to delete stuff.

What is a SAS token?

A SAS token is a string with a specific format like sv=2023-01-03&ss=b&srt=co&st=2024-12-07T18%3A14%3A55Z&se=2024-12-07T20%3A14%3A00Z&sp=rl&sig=1ndGXxufe%2BTOrrnDbDAR%2FDOmoTKnupT8glQfET0QPdY%3D.

Sometimes, Microsoft names things right, and at other times, they need to change the names of products and services. I believe they got the name of Shared Access Signature right. So let's see what it means.

Shared because it is meant to be shared with other parties for delegation purposes.

Access because the SAS token specifies what the holder of it is allowed to do. The sp field stands for signed permissions. As the name implies, it contains the permissions this SAS is signed for, e.g. r means Read, w means Write, d means Delete, and so on. The st(Signed Start) and se(Signed Expiry) specify the time window in which your SAS is valid. Some fields are optional, while others are required. All the fields define the capabilities of your token.

Signature is one special field that typically stays at the end of each SAS token - the sig field. This signature attests to all fields that are specified in the SAS token. The signature is produced by combining all the field values by following a specific order in what's called a string-to-sign which is then signed with a key. This key can either be the Storage account key or a User delegation key. I'll do a deep dive in another post into User delegation SAS where I will delve into more details about how the signature is produced.

There are three main forms in which you can see a SAS token:

  • Use the SAS token itself: Pass the SAS token and specify a URL separately. For example, BlobServiceClient has such a constructor - BlobServiceClient(Uri, AzureSasCredential, BlobClientOptions). Assuming access is properly granted, this approach allows you to target various objects in the account (or blobs in the case of blob storage) while using the same SAS token.
  • In a URL: Append the SAS token to the query string of the URL: https://<your-storage-account>.blob.core.windows.net/<your-container>/<your-file>?sv=2023-01-03&ss=b&srt=co&st=2024-12-07T18%3A14%3A55Z&se=2024-12-07T20%3A14%3A00Z&sp=rl&sig=<signature>. You can open such a URL in your browser and you will access the file. Or by providing a SAS URL that points to a specific container, you can attach that container in Azure Storage Explorer.
  • In a connection string: It's typically used when you want to establish a broader level of access to one or multiple storage services in an Azure Storage account. The SAS token is supplied in a dedicated parameter of the connection string. Here is an example: SharedAccessSignature=sv=2023-01-03&ss=btqf&srt=sco&st=2024-12-07T19%3A42%3A30Z&se=2024-12-08T19%3A42%3A30Z&sp=rl&sig=<signature>;BlobEndpoint=https://<your-storage-account>.blob.core.windows.net/;FileEndpoint=https://<your-storage-account>.file.core.windows.net/;QueueEndpoint=https://<your-storage-account>.queue.core.windows.net/;TableEndpoint=https://<your-storage-account>.table.core.windows.net/;

When you use the SAS token in a request to a particular Azure Storage service, it checks whether the SAS token is valid. More specifically, it ensures that fields in the SAS and the signature belong together. If you try to tamper the fields in the SAS to hopefully gain more permissions, the signature will be invalid. At some point, the SAS token will become invalid because the signed expiry is in the past. So you will need a new SAS token with a value for the se parameter that is in the future. This, of course, results in a new SAS token that contains a new signature.

SAS types

Sometimes it's tricky if you need to choose the type of SAS that you want to use. There are three types of SAS that offer you different options - Account SAS, service SAS, and user delegation SAS. Service and user delegation SAS give you the ability to delegate fine-grained access, for example to a specific file or a specific container, while account SAS allows you to delegate coarsely in the whole account.

I've seen people, as long as they don't care or don't know the differences, opting almost automatically for Account SAS as it allows them to achieve a lot. If you tick most of the checkboxes of Account SAS, it almost becomes as powerful as the storage account key. I hope it is obvious that this is not how you follow the principle of least privilege.

I created a little decision tree that can help you choose the type. The more you stay on the green line towards User delegation SAS, the more secure your SAS is.

The following table summarizes what you can achieve with each type of SAS.

Feature Account SAS Service SAS User delegation SAS
Scope Multi-service (any combination between Blob, Queue, Table, File) Single service Single service
Supported services Blob (blob and dfs endpoints), Queue, Table, File Blob (blob and dfs endpoints), Queue, Table, File Blob (blob and dfs endpoints)
Signed with Account Key Account Key User delegation key
Resource specificity Low (the SAS permissions apply to all services/objects in the account) High (SAS permissions apply to a specific container or object in it) High (SAS permissions apply to a specific container or object in it)
Effective permissions Defined in the SAS token Defined in the SAS token An intersection between the permissions defined in the SAS token and the Azure RBAC role or POSIX ACLs assigned to the Entra ID security principal
Stored Access Policy support No Yes No
Revocation methods Account key rotation - If Stored Access Policy was used to issue the SAS token, modify or delete the policy.
- For ad-hoc SAS, account key rotation.
- Remove the RBAC role of the security principal.
- For accounts with hierarchical namespace enabled, you can remove the POSIX ACLs granted to the security principal.
- Revoke all user delegation keys from the account.
Behavior when AllowSharedKeyAccess=false Request is denied Request is denied Request is allowed
Level of implied security Low Medium Best

4 tips when using a SAS

There are many intricacies and nuances around using a SAS. But following several simple tips can go a long way and keep you out of trouble.

  1. Follow the principle of least privilege. Keep the permissions and the scope of the SAS at the minimum that is required for your use case. For example, if you only need to delegate read-only access to a specific blob from a Blob storage container or a file in a File Share, it doesn't make any sense to generate an Account SAS with all permissions available.
  2. Use a short-lived SAS token, if possible. It can be convenient to generate a long-lived SAS, set it, and forget about it. That's however a terrible idea. Make sure the lifetime of a SAS gives just enough time to the client of that SAS to perform the designated actions but no more. By generating the SAS just before it is needed, you can make sure its lifetime is not too long. That makes sense to be done programmatically by using the respective client libraries.
  3. Treat the SAS token as a secret. SAS tokens must not appear in logs, be present in source code, etc. As a rule of thumb, keep them secure like you do for the storage account key. A SAS while valid can be very limited in the permissions it allows but it might as well give almost identical access as your storage account key does.
  4. Understand the risks. As mentioned just above, a SAS can give away too much power which might not be desirable without having the capabilities to track how many tokens were issued, if they are kept safe, how they are used, or when they expire and need to be renewed. Not to mention, that you'll need to have a plan for revocation of SAS tokens in case they leak. In some cases, you might consider changing the architecture of your solution, such as moving an operation that uses SAS but requires too much power to a tier that can be easily integrated with Entra ID.