Azure blob storage security options in MVC
If you want only your auth. users to have access you have to make the container private. Otherwise it will be public and it is only a matter of time that somebody else gets to the "almost private" content and you as a developer get embarrassed.
Approach 1 : You send a link to your authorized user. In this case you give a SAS link to the user and he downloads his content from the blob directly. You have to generate SAS signatures with short access window so that your users can get your content and download it/ open it and after they are gone from the site the link will expire and the content will be no longer available. This is in case that they accidentally send the link over the wire and somebody else gets to the private content later.
Approach 2 : Your web server gets the content and delivers it to your clients In this case only your web app will have the access and no SAS signatures have to be generated. You return FileContentResult ( in case of MVC ) and you are ready. The downside is that your web server have to download the file prior to giving it to the client - double traffic. Here you have to handle the Blob->Web download carefully because if 3 users try to download a 200 MB file in together and you are storing it in your RAM - it will be depleted.
** UPDATE **
@Intexx provided an updated link to the docs you need.
So, here is what I ended up doing. Thanks to Neil and Ognyan for getting me there.
It works as following:
- All images are private, and cannot be viewed at all without having a valid SAS
- Adding, deletion and modification of blobs are made within the controller itself, all privately. No SAS or additional procedures are needed for these tasks.
- When an image is to be displayed to the user (either anonymously or authenticated), a function generates an SAS with a fast expiry is that merely allows the browser to download the image (or blob), upon page generation and refresh, but not copy/paste a useful URL to the outside.
I first explicitly set the container permissions to Private (this is also the default setting, according to Ognyan):
// Connect to storage account
...
// Retrieve reference to a container.
myContainer= blobClient.GetContainerReference("mycontainer");
// Create the container if it doesn't already exist.
if (myContainer.CreateIfNotExists())
{
// Explicitly configure container for private access
var permissions = myContainer.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Off;
myContainer.SetPermissions(permissions);
}
Then later, when wanting to display the image, I added an SAS string to the original storage path of the blob:
public string GetBlobPathWithSas(string myBlobName)
{
// Get container reference
...
// Get the blob, in my case an image
CloudBlockBlob blob = myContainer.GetBlockBlobReference(myBlobName);
// Generate a Shared Access Signature that expires after 1 minute, with Read and List access
// (A shorter expiry might be feasible for small files, while larger files might need a
// longer access period)
string sas = myContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(1),
Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.List
});
return (blob.Uri.ToString() + sas).ToString();
}
I then called the GetBlobPathWithSas()-function from within the razor view, so that each page refresh would give a valid path+sas for displaying the image:
<img src="@GetPathWithSas("myImage")" />
In general, I found this reference useful:
http://msdn.microsoft.com/en-us/library/ee758387.aspx
Hope that helps someone!
I use Ognyan Dimitrov's "Approach 2" to serve small PDFs stored in a private blob container ("No public read access") inside a browser window like this:
public ActionResult ShowPdf()
{
string fileName = "fileName.pdf";
var storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("containerName");
var blockBlob = container.GetBlockBlobReference(fileName);
Response.AppendHeader("Content-Disposition", "inline; filename=" + fileName);
return File(blockBlob.DownloadByteArray(), "application/pdf");
}
with config file
<configuration>
<appSettings>
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key" />
</appSettings>
</configuration>
...which works perfect for me!