Delete blob content without 'Delete' permissions using shared access signature - azure

i'm using Azure blob storage for storing data from clients.
Clients are given with shared access signature with NO 'Delete' permission.
Nevertheless, i can delete a blob content without 'Delete' permission with the following code:
// sharedKey doesn't contain 'Delete' permission
var credentials = new StorageCredentials(sharedKey);
var blob = new CloudBlockBlob(blobPath, credentials);
var blockIds = new List<string>();
// If not getting all current blocks ids, all current data will be lost.
// if (blob.Exists())
// {
// blockIds.AddRange(blob.DownloadBlockList().Select(b => b.Name));
// }
var blockId =
Convert.ToBase64String(
Encoding.Default.GetBytes(blockIds.Count.ToString("d6", CultureInfo.InvariantCulture)));
blockIds.Add(blockId);
byte[] eventInBytes = Encoding.Default.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\n", formattedEvent));
using (var eventStream = new MemoryStream(eventInBytes))
{
blob.PutBlock(blockId, eventStream, null);
}
blob.PutBlockList(blockIds);
Is this an Azure defect (or i am missing the concept of the shared access signature?. any way to overcome this issue ?
thanks!

The way Share Access Permissions are implemented a user can be granted these access rights: Delete, List, None, Read, Write (See this article). With this level of granularity if you want your user to be able to create a blob, then they will also be able to update a blob. Although you can prevent users from deleting blobs, by issuing a SAS without the delete permission, you cannot prevent users from modifying blobs unless you also deny them the ability to create, both of which are controlled by the "Write" permission.

Related

How to programmatically find out what operations I can do in a blob storage?

I am using libraries Microsoft.Azure.Storage.Blob 11.2.3.0 and Microsoft.Azure.Storage.Common 11.2.3.0 to connect to an Azure BlobStorage from a .NET Core 3.1 application.
When I started working on this, I had been given connection strings that gave me full access to the BlobStorage (or rather, the entire cloud storage account). Based upon those, I chose to write my connection code "defensively", making use of Exists() and CreateIfNotExists() from the CloudBlobContainer class to ensure the application would not fail when a container was not yet existing.
Now, I'm connecting a BlobStorage container using a SAS. While I can freely retrieve and upload blobs within the container like this, unfortunately, it seems that I am not allowed to do anything on the container level. Not only CreateIfNotExists, but even the mere querying of existence by Exists() throws a StorageException saying
This request is not authorized to perform this operation.
The documentation does not mention the exception.
Is there any way to check preemptively whether I am allowed to check the container's existence?
I have tried looking into the container permissions retrieved from GetPermissions, but that will throw an exception, as well.
The only other alternative I can see is to check for container existence within a try-catch-block and assume existence if an exception is thrown ...
There's a no definitive way to identify if an operation can be performed using a SAS token other than performing that operation and catching any exception that may be thrown by the operation. The exception that is of your interest is Unauthorized (403).
However you can try to predict if an operation can be performed by looking at the SAS token. If it is a Service SAS Token and not an Account SAS Token, that means all the account related operations are not not allowed. The way to distinguish between an Account SAS token and a Service SAS token is that the former will contain attributes like SignedServices (ss) and SignedResourceTypes (srt).
Next thing you would want to do is look for SignedPermissions (sp) attribute in your SAS token. This attribute will tell you what all operations are possible with the SAS token. For example, if your SAS token is a Service SAS token and if it includes Delete (d) permission, that would mean you can use this SAS token to delete a blob.
Please see these tables for the permissions/allowed operations combinations:
Service SAS Token: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas#permissions-for-a-directory-container-or-blob
Account SAS Token: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas#permissions-for-a-directory-container-or-blob
Please note that the operation might still fail for any number of reasons like SAS token has expired, account key has changed since the generation of SAS token, IP restrictions etc.
I tried in in my system to check whether the container exist or not able check it and if container not exists created container and able to upload file.
You need to give proper permission for your SAS Token
const string sasToken = “SAS Token”
const string accountName = "teststorage65";
const string blobContainerName = "example";
const string blobName = "test.txt";
const string myFileLocation = #"Local Path ";
var storageAccount = new CloudStorageAccount(storageCredentials, accountName, null, true);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName);
var result=blobContainer.Exists();
if (result == true)
{
Console.WriteLine("Container exists");
}
else
{
// blobContainer.CreateIfNotExists();
Console.WriteLine("Conatiner not exists");
Console.WriteLine("Creating Container "+ blobContainerName);
blobContainer.CreateIfNotExists();
}
// blobContainer.CreateIfNotExists();
//Console.WriteLine("Creating Container ");
CloudBlockBlob cloudBlob = blobContainer.GetBlockBlobReference(blobName);
cloudBlob.UploadFromFile(myFileLocation);
OUTPUT

Download or View file from Azure Blob in Aurelia UI

I have my files stored in the Azure. I want to download or viewing mechanism the file on the client side. Like this:
Azure -> Api -> Client UI (Aurelia)
I have seen lot of c# examples, however I am not sure how to get the file on the UI side. Can anyone please help!
Thanks!
Edit:
Api Code:
public string getUtf8Text()
{
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
var containerName = "myContainer";
var blobName = "myBlobName.pdf";
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);
string text;
using (var memoryStream = new MemoryStream())
{
await blockBlob.DownloadToStreamAsync(memoryStream);
text = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());
return text;
}
}
Trying to download a file, from the utf8 byte string. The client side code is:
var byteCharacters =result.byteArray;
var byteNumbers = new Array(result.byteArray.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var octetStreamMime = "application/octet-stream";
var contentType = octetStreamMime;
var blob = new Blob([byteArray] {type: contentType});
FileSaver.saveAs(blob, result.blobName);
it works sometimes for pdf, rest of the times its just blank pages. It hangs forever for mp4. Any idea whats going on here?
Each blob has a unique URL address. You can use this to display the contents of the blob via a client that can process a URL.
The blob URL will be similar to:
https://myaccount.blob.core.windows.net/mycontainer/myblob
See Naming and Referencing Containers, Blobs, and Metadata for more information.
The greater challenge comes in how you authenticate access to the blob for your users. You have a couple of options:
You can make blobs in the container public, and thus available for anonymous access, without authentication. This means that all blobs in that container will be public. See Manage anonymous read access to containers and blobs.
You can use a shared access signature to delegate access to blobs in the container with the permissions you specify and over the time interval that you specify. This gives you a greater degree of control than anonymous access but also requires more design effort. See Shared Access Signatures, Part 1: Understanding the SAS model.
Note that although anyone possessing your account key can authenticate and access blobs in your account, you should not share your account key with anyone. However, as the account owner, you can access your blobs from your application using authentication with the account key (also known as shared key authentication).

How to restrict azure blob container creation using SAS

How to set permission to not create container, while generating Account SAS token? Here is my settings.
// Create a new access policy for the account.
SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
Permissions = SharedAccessAccountPermissions.Read | SharedAccessAccountPermissions.Write,
Services = SharedAccessAccountServices.Blob | SharedAccessAccountServices.Table,
ResourceTypes = SharedAccessAccountResourceTypes.Service | SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(2),
Protocols = SharedAccessProtocol.HttpsOrHttp
};
Updated answer:
Given that you have multiple containers, the account SAS is a good option. You'll need one for the admin and one for the user.
Here's an example of how to create the admin SAS:
// Create a new access policy for the account.
SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
// SAS for Blob service only.
Services = SharedAccessAccountServices.Blob,
// Admin has read, write, list, and delete permissions on all containers.
// In order to write blobs, Object resource type must also be specified.
ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
Permissions = SharedAccessAccountPermissions.Read |
SharedAccessAccountPermissions.Write |
SharedAccessAccountPermissions.Create |
SharedAccessAccountPermissions.List |
SharedAccessAccountPermissions.Delete,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24),
Protocols = SharedAccessProtocol.HttpsOnly
};
And here's an example of how to create the user SAS:
// Create a new access policy for the account.
SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
// SAS for Blob service only.
Services = SharedAccessAccountServices.Blob,
// User has create, read, write, and delete permissions on blobs.
ResourceTypes = SharedAccessAccountResourceTypes.Object,
Permissions = SharedAccessAccountPermissions.Read |
SharedAccessAccountPermissions.Write |
SharedAccessAccountPermissions.Create |
SharedAccessAccountPermissions.Delete,
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24),
Protocols = SharedAccessProtocol.HttpsOnly
};
Original answer:
You definitely need to use an account SAS for the admin SAS, but you should be able to use a service SAS on the container for the user SAS, unless you have a need for an account SAS that I am not understanding from your question. It's probably better to use the service SAS when you can so that you can use the least complicated permissions. Also, you can use a stored access policy with the service SAS, which we recommend as a best practice so that it's easy to revoke the SAS if it were ever compromised.
With the service SAS, you don't need a permission to restrict container creation, because the service SAS doesn't allow you to create a container in the first place.
Here's code to create the service SAS on the container, including the stored access policy:
// Create the storage account with the connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the blob client object.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Get a reference to the container for which shared access signature will be created.
CloudBlobContainer container = blobClient.GetContainerReference("mycontainer");
container.CreateIfNotExists();
// Create blob container permissions, consisting of a shared access policy
// and a public access setting.
BlobContainerPermissions containerPermissions = container.GetPermissions();
// Clear the container's shared access policies to avoid naming conflicts if you run this method more than once.
//blobPermissions.SharedAccessPolicies.Clear();
// The shared access policy provides
// read/write access to the container for 24 hours.
containerPermissions.SharedAccessPolicies.Add("mypolicy", new SharedAccessBlobPolicy()
{
// To ensure SAS is valid immediately, don’t set start time.
// This way, you can avoid failures caused by small clock differences.
// Note that the Create permission allows the user to create a new blob, as does Write.
SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24),
Permissions = SharedAccessBlobPermissions.Write |
SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Create | SharedAccessBlobPermissions.Delete
});
// The public access setting explicitly specifies that
// the container is private, so that it can't be accessed anonymously.
containerPermissions.PublicAccess = BlobContainerPublicAccessType.Off;
// Set the permission policy on the container.
container.SetPermissions(containerPermissions);
// Get the shared access signature to share with users.
string sasToken =
container.GetSharedAccessSignature(null, "mypolicy");
Take a look at the examples shown here: https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-1/#examples-create-and-use-shared-access-signatures.
Also see https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx and https://msdn.microsoft.com/en-us/library/azure/mt584140.aspx.
Let us know if you have any other questions.

How to use SharedAccessSignature to access blobs

I am trying to access a blob stored in a private container in Windows Azure. The container has a Shared Access Signature but when I try
to access the blob I get a StorgeClientException "Server failed to authenticate the request. Make sure the Authorization header is formed
correctly including the signature".
The code that created the container and uploaded the blob looks like this:
// create the container, set a Shared Access Signature, and share it
// first this to do is to create the connnection to the storage account
// this should be in app.config but as this isa test it will just be implemented
// here:
// add a reference to Microsoft.WindowsAzure.StorageClient
// and Microsoft.WindowsAzure.StorageClient set up the objects
//storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["ConnectionString"]);
blobClient = storageAccount.CreateCloudBlobClient();
// get a reference tot he container for the shared access signature
container = blobClient.GetContainerReference("blobcontainer");
container.CreateIfNotExist();
// now create the permissions policy to use and a public access setting
var permissions = container.GetPermissions();
permissions.SharedAccessPolicies.Remove("accesspolicy");
permissions.SharedAccessPolicies.Add("accesspolicy", new SharedAccessPolicy
{
// this policy is live immediately
// if the policy should be delatyed then use:
//SharedAccessStartTime = DateTime.Now.Add(T); where T is some timespan
SharedAccessExpiryTime =
DateTime.UtcNow.AddYears(2),
Permissions =
SharedAccessPermissions.Read | SharedAccessPermissions.Write
});
// turn off public access
permissions.PublicAccess = BlobContainerPublicAccessType.Off;
// set the permission on the ocntianer
container.SetPermissions(permissions);
var sas = container.GetSharedAccessSignature(new SharedAccessPolicy(), "accesspolicy");
StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sas);
CloudBlobClient client = new CloudBlobClient(storageAccount.BlobEndpoint,
new StorageCredentialsSharedAccessSignature(sas));
CloudBlob sasblob = client.GetBlobReference("blobcontainer/someblob.txt");
sasblob.UploadText("I want to read this text via a rest call");
// write the SAS to file so I can use it later in other apps
using (var writer = new StreamWriter(#"C:\policy.txt"))
{
writer.WriteLine(container.GetSharedAccessSignature(new SharedAccessPolicy(), "securedblobpolicy"));
}
The code I have been trying to use to read the blob looks like this:
// the storace credentials shared access signature is copied directly from the text file "c:\policy.txt"
CloudBlobClient client = new CloudBlobClient("https://my.azurestorage.windows.net/", new StorageCredentialsSharedAccessSignature("?sr=c&si=accesspolicy&sig=0PMoXpht2TF1Jr0uYPfUQnLaPMiXrqegmjYzeg69%2FCI%3D"));
CloudBlob blob = client.GetBlobReference("blobcontainer/someblob.txt");
Console.WriteLine(blob.DownloadText());
Console.ReadLine();
I can make the above work by adding the account credentials but that is exactly what I'm trying to avoid. I do not want something
as sensitive as my account credentials just sitting out there and I have no idea on how to get the signature into the client app without having the account credentials.
Any help is greatly appreciated.
Why this?
writer.WriteLine(container.GetSharedAccessSignature(new SharedAccessPolicy(), "securedblobpolicy"));
and not writing the sas string you already created?
It's late and I could easily be missing something but it seems that you might not be saving the same access signature that you're using to write the file in the first place.
Also perhaps not relevant here but I believe there is a limit on the number of container-wide policies you can have. Are you uploading multiple files to the same container with this code and creating a new container sas each time?
In general I think it would be better to request a sas for an individual blob at the time you need it with a short expiry time.
Is "my.azurestorage.windows.net" just a typo? I would expect something there like "https://account.blob.core.windows.net".
Otherwise the code looks pretty similar to the code in http://blog.smarx.com/posts/shared-access-signatures-are-easy-these-days, which works.

Azure Storage Connection Check

I have a console application which uploads jobs to the workers running in the cloud. The application connects to Azure Storage and uploads some files to blobs and put some messages to queues. Currently, I am using the development storage. I actually want to know at which state my client application connects to the storage. Can I create a queueClient even though I do not have any connection at all? At which step it actually tries to send some network packages? I actually need some kind of a mechanism in order to check the existence of the connection and the validity of the storage account.
The client doesn't send any messages until you call a command on the storage - e.g. until you try to get or put a property of a blob, container, or queue - e.g. in the sample code below (from http://msdn.microsoft.com/en-us/library/gg651129.aspx) then messages are sent in 3 specific places:
// Variables for the cloud storage objects.
CloudStorageAccount cloudStorageAccount;
CloudBlobClient blobClient;
CloudBlobContainer blobContainer;
BlobContainerPermissions containerPermissions;
CloudBlob blob;
// Use the local storage account.
cloudStorageAccount = CloudStorageAccount.DevelopmentStorageAccount;
// If you want to use Windows Azure cloud storage account, use the following
// code (after uncommenting) instead of the code above.
// cloudStorageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=http;AccountName=your_storage_account_name;AccountKey=your_storage_account_key");
// Create the blob client, which provides
// authenticated access to the Blob service.
blobClient = cloudStorageAccount.CreateCloudBlobClient();
// Get the container reference.
blobContainer = blobClient.GetContainerReference("mycontainer");
// Create the container if it does not exist.
// MESSAGE SENT
blobContainer.CreateIfNotExist();
// Set permissions on the container.
containerPermissions = new BlobContainerPermissions();
// This sample sets the container to have public blobs. Your application
// needs may be different. See the documentation for BlobContainerPermissions
// for more information about blob container permissions.
containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob;
// MESSAGE SENT
blobContainer.SetPermissions(containerPermissions);
// Get a reference to the blob.
blob = blobContainer.GetBlobReference("myfile.txt");
// Upload a file from the local system to the blob.
Console.WriteLine("Starting file upload");
// MESSAGE SENT
blob.UploadFile(#"c:\myfiles\myfile.txt"); // File from local storage.
Console.WriteLine("File upload complete to blob " + blob.Uri);
One way to check whether you have connectivity is to use some of the simple functions like CreateIfNotExist() or GetPermissions() on a known container - these give you a simple quick method to check connectivity. You can also use the BlobRequestOptions to specify a timeout to make sure your app doesn't hang (http://msdn.microsoft.com/en-us/library/ee772886.aspx)
Be careful not to check connectivity too frequently - every 10000 successful checks will cost you $0.01
Adding to what Stuart has written above, what you can do is try and list 1 queue from your storage account using CloudQueueClient.ListQueuesSegmented Method (http://msdn.microsoft.com/en-us/library/ff361716.aspx). We're really not interested in the result as such. What we're more interested in is if we get a storage client exception or not which you will get if the credentials are not correct. Even if you don't have a queue in your storage account, as long as you have passed correct credentials you will not get an error.
Somehow I don't feel creating an object would be the right thing to test storage account credentials. In the past we've seen situations where a storage account was made read only and even if you pass valid credentials and try to see if the credentials are correct by creating an object, you would get an error.
Hope this helps.
Here's how we check if the connection is ready:
public async Task<bool> IsReady()
{
var options = new BlobRequestOptions { ServerTimeout = TimeSpan.FromSeconds(2), MaximumExecutionTime = TimeSpan.FromSeconds(2) };
var container = GetStorageClient().GetContainerReference("status");
var blobRef = container.GetBlockBlobReference("health-check");
try
{
await blobRef.DownloadTextAsync(null, options, null);
}
catch (StorageException e)
{
if (e.RequestInformation.HttpStatusCode != 404)
return false;
try
{
await container.CreateIfNotExistsAsync(options, null);
await blobRef.UploadTextAsync("health check", AccessCondition.GenerateEmptyCondition(), options, null);
}
catch (Exception)
{
return false;
}
}
return true;
}
GetStorageClient() is a custom method, replace with your own
What it does:
We try to read a test file because reading is much cheaper than write operations
If the exception is "file not found" let's try to create the file
If exception is other than 404 we can stop here, trying to write should make no sense
Note: When using "cool" storage using an "other operation" would be cheaper. See https://azure.microsoft.com/en-us/pricing/details/storage/blobs/

Resources