3
votes

I'm trying to implement shared access signatures when saving blobs (pdf files) to azure blob storage. I want the link to the pdf file to expire after a set time, but it doesn't seem to be working.

The pdf creation and save process works fine, I create a pdf file and upload it to azure blob storage. I can retrieve the blob URL and if I paste it into a browser, the pdf report shows up ok. It never expires though.

I set the expiry time to 2 minutes while Im testing (it would be around 24 hours in production). I can continue to view the report, nothing stops me.

I am new to shared signature access, but from what I've found so far, it is supposed to stop access after the specified time(is this correct?).

This is how I create the storage details (in the constructor of my class):

public BlobService()
{
    //use for local development testing
    _connectionString = Settings.AzureWebJobsStorage;
    this._container = Settings.ReportBlobContainer;

    try
    {
        storageAccount = CloudStorageAccount.Parse(_connectionString);
    }
    catch (StorageException e)
    {
        throw;
    }

    // Get an account SAS token.
    string sasToken = GetAccountSASToken();

    // Use the account SAS token to create authentication credentials.
    StorageCredentials accountSAS = new StorageCredentials(sasToken);

    var blobClient = storageAccount.CreateCloudBlobClient();

    chpBlobContainer = blobClient.GetContainerReference(this._container);
    // Get the URI for the container.
    Uri containerUri = GetContainerUri();
    chpBlobContainer = new CloudBlobContainer(containerUri, accountSAS);

    try
    {
        if (chpBlobContainer.CreateIfNotExists())
        {
            //leave the access to private only (default)
            // Enable public access on the newly created container.
            //chpBlobContainer.SetPermissions(
            //    new BlobContainerPermissions
            //    {
            //        PublicAccess = BlobContainerPublicAccessType.Blob
            //    });
        }
    }
    catch(Exception ex)
    {
        var tmp = ex.Message;
    }

}

and this is how I generate the SAS token

private string GetAccountSASToken()
{
    // Retrieve storage account information from connection string
    //CloudStorageAccount storageAccount = Common.CreateStorageAccountFromConnectionString();

    // Create a new access policy for the account with the following properties:
    // Permissions: Read, Write, List, Create, Delete
    // ResourceType: Container
    // Expires in 24 hours
    // Protocols: HTTPS or HTTP (note that the storage emulator does not support HTTPS)
    SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
    {
        // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. 
        // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew.
        //Permissions = SharedAccessAccountPermissions.Read | SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.List | SharedAccessAccountPermissions.Create | SharedAccessAccountPermissions.Delete,
        Permissions = SharedAccessAccountPermissions.Read | SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.Create,
        Services = SharedAccessAccountServices.Blob,
        ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
        //SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24),
        //just for testing the expiry works
        SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(2),
        Protocols = SharedAccessProtocol.HttpsOrHttp
    };

    // Create new storage credentials using the SAS token.
    string sasToken = storageAccount.GetSharedAccessSignature(policy);

    // Return the SASToken
    return sasToken;
}

I can see the blob from the Azure storage explorer, so the connection and generation process is fine, its just the expiry.

Can anyone help me out? I am clearly doing something wrong here.

1
It looks like the ACL for the blob container is not Private. Can you please check that? Also, if possible please share the SAS URL. - Gaurav Mantri
Another possible reason could be that the browser has cached the file. Please try with some other browser. - Gaurav Mantri
isn't it created private by default ?, this should create it as private if I'm correct chpBlobContainer.CreateIfNotExists() also I tried a different browser and its still accessible - proteus
I cant share the URL as its local development using the storage emulator - proteus

1 Answers

4
votes

As Gaurav Mantri mentioned that it seems that your container is not private. There are 3 types of permissions for a container: public,blob,private. We could get more info from Set Container ACL.

The permissions indicate whether blobs in a container may be accessed publicly. Beginning with the 2009-09-19 version, the container permissions provide the following options for managing container access:

Full public read access: Container and blob data can be read via anonymous request. Clients can enumerate blobs within the container via anonymous request, but cannot enumerate containers within the storage account.

Public read access for blobs only: Blob data within this container can be read via anonymous request, but container data is not available. Clients cannot enumerate blobs within the container via anonymous request.

No public read access: Container and blob data can be read by the account owner only.

If the container is not private, we could set container ACL with Microsoft Azure Storage Explorer easily.

enter image description here

enter image description here

Updated:

By default the container permission is private, if we want to set permission for container programmatically. Please have a try to using the following code.

  container.SetPermissions(new BlobContainerPermissions
                {
                    PublicAccess = BlobContainerPublicAccessType.Off //private
                });

Or please have a try to create a new container and try to create and use the SAS token to access the blob again.

Update2 :

Please have a try to test with following demo code, we also could get it from Azure official document, it works correctly on my side:

 var connectionString = "xxxxxxxxxxxxxx"; //UseDevelopmentStorage=true
 CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
 CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
 CloudBlobContainer container = blobClient.GetContainerReference("testcontainer");
 container.CreateIfNotExists();
 var sasBlobUri = GetBlobSasUri(container, @"C:\Tom\test.pdf");
 Console.WriteLine(sasBlobUri);
 Console.ReadKey();

 static string GetBlobSasUri(CloudBlobContainer container,string filePath)
 {

            if (!container.GetPermissions().PublicAccess.Equals(BlobContainerPublicAccessType.Off))
            {
                container.SetPermissions(new BlobContainerPermissions
                {
                    PublicAccess = BlobContainerPublicAccessType.Off
                });
            }
            var blobName = Path.GetFileName(filePath);
            //Get a reference to a blob within the container.
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);

            //Upload text to the blob. If the blob does not yet exist, it will be created.
            //If the blob does exist, its existing content will be overwritten.
            blob.UploadFromFile(filePath);

            //Set the expiry time and permissions for the blob.
            //In this case, the start time is specified as a few minutes in the past, to mitigate clock skew.
            //The shared access signature will be valid immediately.
            SharedAccessBlobPolicy sasConstraints =
                new SharedAccessBlobPolicy
                {
                    SharedAccessStartTime = DateTimeOffset.UtcNow.AddMinutes(-5),
                    SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddMinutes(2), // 2 minutes expired
                    Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write  //Read & Write
                };

            //Generate the shared access signature on the blob, setting the constraints directly on the signature.
            string sasBlobToken = blob.GetSharedAccessSignature(sasConstraints);

            //Return the URI string for the container, including the SAS token.
            return blob.Uri + sasBlobToken;
    }

After 2 minutes I check from the incognito chrome window.

enter image description here