How do you generate the signature for an Azure Blob storage SAS token in Python?
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)
Comments
-
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:
Still, the URL fails with a 403. Any idea on what I am doing wrong?
-
adrianp over 7 yearsLooked 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 almost 5 yearsBroken link... :(
-
Jordan Simba over 4 yearsWhat are good reasons to not use the SDK if already using their services?
-
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 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 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 over 3 yearsThis 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 over 3 years@JordanSimba good points all around. Thanks for the feedback and perspective.