How do you generate the signature for an Azure Blob storage SAS token in Python?

10,231

Solution 1

Based on the documentation (Please see Constructing the Signature String section), the parameters passed to string to sign must be URL decoded. From the link:

To construct the signature string of a shared access signature, first construct the string-to-sign from the fields comprising the request, then encode the string as UTF-8 and compute the signature using the HMAC-SHA256 algorithm. Note that fields included in the string-to-sign must be URL-decoded.

Please use un-encoded parameter values in your string to sign and that should fix the problem.

Solution 2

Update, with the latest storage python library, this is what I used to generate the sas token:

def generate_sas_token(file_name):
    sas = generate_blob_sas(account_name=AZURE_ACC_NAME,
                            account_key=AZURE_PRIMARY_KEY,
                            container_name=AZURE_CONTAINER,
                            blob_name=file_name,
                            permission=BlobSasPermissions(read=True),
                            expiry=datetime.utcnow() + timedelta(hours=2))

    logging.info('https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+file_name+'?'+sas)
    sas_url ='https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+file_name+'?'+sas
    return sas_url

Python 3.6 and azure-storage-blob package.

Solution 3

The easiest way to generate SAS token in python is to leverage Azure Storage SDK for Python. Please consider following code snippet:

import time
import uuid
import hmac
import base64
import hashlib
import urllib
from datetime import datetime, timedelta
from azure.storage import (
    AccessPolicy,
    ResourceTypes,
    AccountPermissions,
    CloudStorageAccount,
)
from azure.storage.blob import (
    BlockBlobService,
    ContainerPermissions,
    BlobPermissions,
    PublicAccess,
)

AZURE_ACC_NAME = '<account_name>'
AZURE_PRIMARY_KEY = '<account_key>'
AZURE_CONTAINER = '<container_name>'
AZURE_BLOB='<blob_name>'

def generate_sas_with_sdk():
    block_blob_service = BlockBlobService(account_name=AZURE_ACC_NAME, account_key=AZURE_PRIMARY_KEY)    
    sas_url = block_blob_service.generate_blob_shared_access_signature(AZURE_CONTAINER,AZURE_BLOB,BlobPermissions.READ,datetime.utcnow() + timedelta(hours=1))
    #print sas_url
    print 'https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+AZURE_BLOB+'?'+sas_url

generate_sas_with_sdk()

Furthermore, to generate SAS token via plain python script, you can refer to the source code at https://github.com/Azure/azure-storage-python/blob/master/azure/storage/sharedaccesssignature.py#L173 for more hints.

Solution 4

Here's an updated code snippet for Python3 and the updated Azure Storage Blob SDK:

from datetime import datetime, timedelta
from azure.storage.blob import (
    BlockBlobService,
    ContainerPermissions,
    BlobPermissions,
    PublicAccess,
)

AZURE_ACC_NAME = '<account_name>'
AZURE_PRIMARY_KEY = '<account_key>'
AZURE_CONTAINER = '<container_name>'
AZURE_BLOB='<blob_name>'

block_blob_service = BlockBlobService(account_name=AZURE_ACC_NAME, account_key=AZURE_PRIMARY_KEY)
sas_url = block_blob_service.generate_blob_shared_access_signature(AZURE_CONTAINER,AZURE_BLOB,permission=BlobPermissions.READ,expiry= datetime.utcnow() + timedelta(hours=1))
print('https://'+AZURE_ACC_NAME+'.blob.core.windows.net/'+AZURE_CONTAINER+'/'+AZURE_BLOB+'?'+sas_url)
Share:
10,231
adrianp
Author by

adrianp

SOreadytohelp

Updated on June 24, 2022

Comments

  • adrianp
    adrianp almost 2 years

    I am trying to build the SAS token required for a blob download URL in Python, following the instructions from MSDN.

    My string to sign looks like:

    r\n
    2016-12-22T14%3A00%3A00Z\n
    2016-12-22T15%3A00%3A00Z\n
    %2Fblob%2Fmytest%2Fprivatefiles%2F1%2Fqux.txt\n
    \n
    \n
    https\n
    2015-12-11\n
    \n
    \n
    \n
    \n
    _
    

    I've added the newline symbols for clarity and the last line is supposed to be an empty line (with no newline at the end).

    The Python method I use for signing the string is:

    def sign(self, string):
        hashed = hmac.new(base64.b64decode(self.account_key), digestmod=sha256)
        hashed.update(string)
        base64_str = base64.encodestring(hashed.digest()).strip()
        return base64_str
    

    The final URL I build looks like:

    https://mytest.blob.core.windows.net/privatefiles/1/qux.txt?sv=2015-12-11&st=2016-12-22T14%3A00%3A00Z&se=2016-12-22T15%3A00%3A00Z&sr=b&sp=r&spr=https&sig=BxkcpoRq3xanEHwU6u5%2FYsULEtOCJebHmupUZaPmBgM%3D

    Still, the URL fails with a 403. Any idea on what I am doing wrong?

  • adrianp
    adrianp over 7 years
    Looked over their code (the more precise reference is this class) but I could not figure out what they are doing differently; I want to have my code working and not use the SDK for objective reasons.
  • jtlz2
    jtlz2 almost 5 years
    Broken link... :(
  • Jordan Simba
    Jordan Simba over 4 years
    What are good reasons to not use the SDK if already using their services?
  • dreftymac
    dreftymac over 3 years
    @jordan simba //What are good reasons to not use the SDK// because using pure HTTP REST-API approach has fewer dependencies and is arguably "closer to the metal" closer to the metal
  • Jordan Simba
    Jordan Simba over 3 years
    @dreftymac The way I think about it is similar to why I would use a library to URL encode a string, rather than replicate a standard already implemented and maintained in a library/ module. Less room for mistake, less maintenance, less area to test etc...
  • dreftymac
    dreftymac over 3 years
    @JordanSimba Yep, that makes sense. You are actually making a good point in favor of not using an SDK. Why? Because, in some cases, the SDK can actually have higher maintenance requirements compared to using a pure REST API based approach. Of course, it depends on the SDK (how new it is, which language, etc) but it is not always the case that the SDK approach entails less maintenance.
  • Jordan Simba
    Jordan Simba over 3 years
    This is true. But SDKs exist for convenience, if one makes use of any of th SDKs offerings, you might as well use all! So if the OP is using a completely HTTP approach, then I agree they should stay in keeping with that, otherwise use the tool already in the toolkit you've paid for.
  • dreftymac
    dreftymac over 3 years
    @JordanSimba good points all around. Thanks for the feedback and perspective.