Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / Azure

Securing Azure Service Bus Queues with Shared Access Signatures

5.00/5 (3 votes)
21 Jan 2016CPOL3 min read 13K  
How to generate and use Shared Access Signatures for Azure Service Bus Queues

Introduction

In this tip, I show how you can secure access to a Microsoft Azure Service Bus queue by generating a Shared Access Signature.

Why Shared Access Signatures?

Whenever you’re working with resources in the cloud, whether you’re reading and writing data in blob storage or publishing and subscribing to messages on a queue, it’s always best to work with the “principle of least privilege”. In Microsoft Azure, this is achieved by using “Shared Access Signatures”.

A Shared Access Signature gives the holder of that signature access to a particular resource (like a blob or a queue), for a limited time, and with limited permissions (e.g. read only). The advantage is that you don’t need to give someone the full “connection string” just to allow them write to a specific blob, or post to a particular queue.

I have an article on my blog about how you can control access to an Azure Blob container with Shared Access Signatures, and it is relatively straightforward. However, when I tried to do the same for a Service Bus namespace, it turned out to be a bit trickier than I hoped, as the MSDN documentation omits certain key details.

So in this tip, I’ll demonstrate how we can create a Shared Access Signature that we can give someone to allow them to read messages off a specific queue.

Creating a Shared Access Signature

First of all, we need to create an authorization rule for our queue. You don’t need to create one for every SAS token you want to generate, just one for each set of permissions you want to grant.

Typically, you’d create one with “Listen” rights, and one with “Send” rights. The following code snippet shows us checking if the queue exists, and creating it if needed, and then checking if the shared access authorization rule exists, and creating it if needed. (Obviously, this rule is created by a process that does have the privileges to create shared access authorization rules).

C#
// service bus connection string
var connectionString = "Endpoint=sb://mynamespace.servicebus.windows.net/;
SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=sgjasdhgjkasdhgawe3857238212";
const string queueName = "MyTestQueue";

// first ensure the queue exists
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.QueueExists(queueName))
{
    namespaceManager.CreateQueue(queueName);
}

// make a shared authorization rule which will have listen privileges
var sharedAccessAuthorizationRuleKeyName = "MyQueue_Listen"; 

// find out if the authorization rule already exists:
var queueDescription = namespaceManager.GetQueue(queueName);
var authRule = queueDescription.Authorization
    .OfType<SharedAccessAuthorizationRule>()
    .FirstOrDefault(a => a.KeyName == sharedAccessAuthorizationRuleKeyName);
    
// if it doesn't:
if (authRule == null)
{
    // create a new authorization rule, with listen rights 
    authRule = new SharedAccessAuthorizationRule(
        sharedAccessAuthorizationRuleKeyName,
        SharedAccessAuthorizationRule.GenerateRandomKey(), // generate a primary key for this rule
        new[] { AccessRights.Listen }); // this rule only allows listening
        
    // add this to the list of authorization rules
    queueDescription.Authorization.Add(authRule);
    // actually update the queue with the new list of rules
    namespaceManager.UpdateQueue(queueDescription);
}

Now we’re ready to create the shared access signature itself (with thanks to Brent for pointing me in the right direction here). We can choose an expiry time for the signature, which ideally would be fairly short. And we generate the Shared Access Signature like this:

C#
var expiry = TimeSpan.FromMinutes(30);
var serviceUri = ServiceBusEnvironment.CreateServiceUri
("https", "mynamespace", queueName).ToString().Trim('/');
string generatedSaS = SharedAccessSignatureTokenProvider
          .GetSharedAccessSignature(sharedAccessAuthorizationRuleKeyName,
?                                    authRule.PrimaryKey, serviceUri, expiry);

Now we have a signature which will look something like this:

SharedAccessSignature sr=https%3a%2f%2fmynamespace.servicebus.windows.net%2fMyTestQueue&
sig=fFWmdMmWjsdTqPyhyvRS9LQqLjJNPc87xhInhYai9OM%3d&se=1453286209&skn=MyQueue_Listen

This is the string we can give to the client we want to grant access to. Obviously it’s still a secret, but if it does leak, the potential damage is limited to only listening on this one queue, and only for the specified duration.

Using the Shared Access Signature

Now we’re ready to actually use the Shared Access Signature, and we do that with the TokenProvider and MessagingFactory. These will allow us to create a QueueClient which we can use to receive a message (since our Shared Access Authorization Rule specified listen permissions).

C#
var tp = TokenProvider.CreateSharedAccessSignatureTokenProvider(generatedSaS);
var mf = MessagingFactory.Create("sb://mynamespace.servicebus.windows.net", tp);
var listenClient = mf.CreateQueueClient(queueName);

// receive a message
var rxMessage = listenClient.Receive(TimeSpan.FromSeconds(10));

And for completeness, let’s check that our permissions really worked. We’ll try to send a message on this listen client. And we’ll try to create a new listen client for another queue that we weren’t granted access to:

C#
try
{
    listenClient.Send(new BrokeredMessage("should not be sent"));
    throw new InvalidOperationException("SHOULD HAVE BEEN BLOCKED");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Very good - can't send");
}

try
{
    var listenClient2 = mf.CreateQueueClient("AnotherQueue");
    var m = listenClient2.Receive();
    throw new InvalidOperationException("SHOULD HAVE BEEN BLOCKED");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Very good - can't listen on another queue");
}

Anyway, that’s it for creating Shared Access Signatures with Azure Service Bus queues. For topics, it’s a little more involved as you’ll want to grant manage rights to allow the holder of the SAS token to create a subscription, but otherwise the process is roughly the same.

History

  • 21 Jan 2016 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)