0

I want to get a reference to the blob and generate a SAS URL for it.

How? Without exposing my storage account key?

What all have I tried? Getting the reference to blob by using SAS (of blob container or storage account). My references: https://learn.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1?toc=%2fazure%2fstorage%2fblobs%2ftoc.json

The exception that I see: "Can not create Shared Access Signature unless Account Key credentials are used"

But I do not (obviously) want to expose my account key! Is this even possible? If not, is there any other way of doing it?

4
  • You could implement an API that runs in Azure and has one of your two account keys to generate a SAS token. This way it will stay insied Azure. Or store the key in Key Vault and retrieve it from there as soon as you need to generate a token. Commented Sep 28, 2017 at 10:03
  • Are there any other ways of using SAS to generate blob SAS? @RickvandenBosch Commented Sep 28, 2017 at 10:37
  • 1
    A SAS doesn't expose your api key. You need your api key when creating a SAS. You'd never have to share your key publicly - you'd create a SAS within your app. Commented Sep 28, 2017 at 11:15
  • Hi, you can check this example github.com/Azure-Samples/functions-storage-managed-identity/… Commented Mar 14, 2022 at 13:09

2 Answers 2

6

In short: no, there's no other way to do that besides using one of the keys. You need one of the Access Keys to be able to create a SAS token. Here's why you cannot do that with an existing SAS token:

The signature is an HMAC computed over the string-to-sign and key using the SHA256 algorithm, and then encoded using Base64 encoding.

This means the signature that is part of your SAS token is a calculated value. Part of that calculation is based on (one of the) key(s), since that is used to calculate the non-reversible hash. The fact that this hash is non-reversible means you cannot retrieve the Access Key used to calculate the hash. And therefor, you cannot use a SAS token to create another SAS token: you don't have an Access Key available to calculate the signature.

When you create a storage account, you get two storage access keys, which provide full control over the storage account contents. These keys are admin credentials.

More information: Constructing a Service SAS

Sign up to request clarification or add additional context in comments.

3 Comments

Oh. That means that if you regenerate your account key, the SASs are useless.
I have a azure function to generate the sas token, but it still need the access key, and for that I need to add it to the environment, is there any way to do this by code?
@ErnestoAlfonso taken from documentation: Both a service SAS and an account SAS are signed with the storage account key. To create a SAS that is signed with the account key, an application must have access to the account key. To secure your Access Key, have a look at Azure Key Vault.
0

This is an interpretation of this example

First we have this to get the accountKey:

    public static async Task<StorageAccountKey> GetAccountKeys(string KeyName)
        {
            IAzure storageAccounts;
            if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(@"AZURE_TENANT_ID"))
                && !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(@"AZURE_SUBSCRIPTION_ID")))
            {
                storageAccounts = GetStorageAccountWithTenantAndSubscription();
            }
            else
            {

                AzureCredentials credentials = SdkContext.AzureCredentialsFactory.FromSystemAssignedManagedServiceIdentity(MSIResourceType.AppService, AzureEnvironment.AzureGlobalCloud);
                storageAccounts = Microsoft.Azure.Management.Fluent.Azure
                    .Authenticate(credentials)
                    .WithDefaultSubscription();

            }

            IStorageAccount storageAccount = await storageAccounts.StorageAccounts.GetByResourceGroupAsync(
                Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_GROUP"),
                Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_NAME")
            );
            IReadOnlyList<StorageAccountKey> accountKeys = storageAccount.GetKeys();

            return accountKeys.FirstOrDefault(k => k.KeyName == KeyName);
        }

        private static IAzure GetStorageAccountWithTenantAndSubscription()
        {
            DefaultAzureCredential tokenCred = new DefaultAzureCredential(includeInteractiveCredentials: true);
            string armToken = tokenCred.GetToken(new TokenRequestContext(scopes: new[] { "https://management.azure.com/.default" }, parentRequestId: null), default).Token;
            TokenCredentials armCreds = new TokenCredentials(armToken);

            string graphToken = tokenCred.GetToken(new TokenRequestContext(scopes: new[] { "https://graph.windows.net/.default" }, parentRequestId: null), default).Token;
            TokenCredentials graphCreds = new TokenCredentials(graphToken);

            AzureCredentials credentials = new AzureCredentials(armCreds, graphCreds, Environment.GetEnvironmentVariable(@"AZURE_TENANT_ID"), AzureEnvironment.AzureGlobalCloud);

            return Microsoft.Azure.Management.Fluent.Azure
                .Authenticate(credentials)
                .WithSubscription(Environment.GetEnvironmentVariable(@"AZURE_SUBSCRIPTION_ID"));
        }

Where you need to define the next environment variables:

AZURE_TENANT_ID
AZURE_SUBSCRIPTION_ID
STORAGE_ACCOUNT_GROUP
STORAGE_ACCOUNT_NAME

All of them can be found on the https://portal.azure.com/ and if you run az login

then you can do this to generate the connection string:

        private static async Task<string> GetAccountSASToken()
        {
            StorageAccountKey accountKeyObj = await GetAccountKeys(Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_KEY"));
            string accountKey = accountKeyObj.Value;
            string accountName = Environment.GetEnvironmentVariable("STORAGE_ACCOUNT_NAME");
            StorageSharedKeyCredential key = new StorageSharedKeyCredential(accountName, accountKey);
            AccountSasBuilder sasBuilder = new AccountSasBuilder()
            {
                Services = AccountSasServices.Blobs | AccountSasServices.Files,
                ResourceTypes = AccountSasResourceTypes.Container | AccountSasResourceTypes.Object,
                ExpiresOn = DateTimeOffset.UtcNow.AddHours(1),
                Protocol = SasProtocol.Https
            };

            sasBuilder.SetPermissions(AccountSasPermissions.List | AccountSasPermissions.Read);
            string sasToken = sasBuilder.ToSasQueryParameters(key).ToString();

            return sasToken;
        }

And that's all you need.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.