Service Bus Security with SAS and RBAC
Azure Service Bus provides two mechanisms to control access to messaging resources — Shared Access Signatures (SAS) and Role-Based Access Control (RBAC). SAS uses key-based authorization where a connection string or token grants access. RBAC uses Azure Active Directory identities — users, service principals, or managed identities — to grant permissions without any secrets or keys.
Why Security Matters in Messaging
Service Bus queues and topics carry business data — orders, payments, user events. Unauthorized access to these resources can leak sensitive data, inject malicious messages, or disrupt business workflows. Proper authentication and authorization prevent all three risks.
Security Method Comparison
| Method | Uses Secret? | Granularity | Recommended For |
|---|---|---|---|
| SAS (Connection String) | Yes — key embedded in string | Namespace or entity level | Dev/test, legacy apps, external clients |
| SAS Token | Yes — signed token with expiry | Namespace or entity level | Short-lived external access |
| RBAC + Managed Identity | No — uses Azure AD identity | Namespace, queue, topic, subscription | Production Azure services (recommended) |
| RBAC + Service Principal | Yes — client secret or cert | Namespace, queue, topic, subscription | External services needing Azure AD |
Part 1 — Shared Access Signatures (SAS)
What Is SAS?
A Shared Access Signature is a URI or token that grants time-limited, scoped access to a Service Bus resource. It is based on a cryptographic key stored in a Shared Access Policy. The policy defines which operations the key permits — Send, Listen, or Manage.
SAS Permission Types
| Permission | Allows | Use Case |
|---|---|---|
| Send | Send messages to queues or topics | Producer applications |
| Listen | Receive messages from queues and subscriptions | Consumer applications |
| Manage | Create, delete, and update queues and topics (includes Send + Listen) | Admin tools, DevOps pipelines |
Create a Shared Access Policy — Azure CLI
# Create a Send-only policy for producers az servicebus queue authorization-rule create \ --resource-group rg-messaging-prod \ --namespace-name myshopns \ --queue-name orders \ --name producer-policy \ --rights Send # Create a Listen-only policy for consumers az servicebus queue authorization-rule create \ --resource-group rg-messaging-prod \ --namespace-name myshopns \ --queue-name orders \ --name consumer-policy \ --rights Listen
Get the Connection String for a Policy
az servicebus queue authorization-rule keys list \ --resource-group rg-messaging-prod \ --namespace-name myshopns \ --queue-name orders \ --name producer-policy \ --query primaryConnectionString \ --output tsv
Connection String Structure
Endpoint=sb://myshopns.servicebus.windows.net/; SharedAccessKeyName=producer-policy; SharedAccessKey=<base64-encoded-key>; EntityPath=orders
Use Connection String in .NET
string connectionString = "Endpoint=sb://myshopns.servicebus.windows.net/;SharedAccessKeyName=producer-policy;SharedAccessKey=<key>;EntityPath=orders";
await using var client = new ServiceBusClient(connectionString);
await using var sender = client.CreateSender("orders");
await sender.SendMessageAsync(new ServiceBusMessage("Test order"));
Console.WriteLine("Message sent using SAS connection string.");
Generate a SAS Token Programmatically
// Generate a SAS token valid for 1 hour
private static string GenerateSasToken(string resourceUri, string keyName, string key)
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + 3600); // 1 hour
string stringToSign = Uri.EscapeDataString(resourceUri) + "\n" + expiry;
var hmac = new System.Security.Cryptography.HMACSHA256(
Encoding.UTF8.GetBytes(key)
);
var signature = Convert.ToBase64String(
hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign))
);
return $"SharedAccessSignature sr={Uri.EscapeDataString(resourceUri)}" +
$"&sig={Uri.EscapeDataString(signature)}" +
$"&se={expiry}&skn={keyName}";
}
SAS Best Practices
- Never use the
RootManageSharedAccessKeyin application code — it has full access to the namespace - Create separate policies per role — one for Send, one for Listen
- Create policies at the entity level (queue or topic), not the namespace level, for least-privilege access
- Rotate keys regularly — Azure provides a primary and secondary key for zero-downtime rotation
- Store connection strings in Azure Key Vault — never in source code or config files
Part 2 — Role-Based Access Control (RBAC)
What Is RBAC for Service Bus?
RBAC grants permissions to Azure Active Directory (AAD) identities — users, groups, managed identities, or service principals — using predefined roles. No connection strings or keys are needed. The application authenticates using its AAD identity and Azure checks if it has the required role to access the Service Bus entity.
Built-In Service Bus RBAC Roles
| Role | Permissions | Use Case |
|---|---|---|
| Azure Service Bus Data Owner | Full access — Send, Receive, Manage | Admin-level tools, infrastructure deployments |
| Azure Service Bus Data Sender | Send messages only | Producer services |
| Azure Service Bus Data Receiver | Receive and settle messages only | Consumer services |
Assign RBAC Role to a Managed Identity — Azure CLI
# Get the namespace resource ID NAMESPACE_ID=$(az servicebus namespace show \ --resource-group rg-messaging-prod \ --name myshopns \ --query id --output tsv) # Assign "Data Sender" role to a managed identity (App Service, Function App, etc.) az role assignment create \ --assignee <managed-identity-object-id> \ --role "Azure Service Bus Data Sender" \ --scope $NAMESPACE_ID
Assign Role at Queue Level (Least Privilege)
# Get the queue resource ID QUEUE_ID=$(az servicebus queue show \ --resource-group rg-messaging-prod \ --namespace-name myshopns \ --name orders \ --query id --output tsv) # Assign "Data Receiver" role only to the orders queue az role assignment create \ --assignee <managed-identity-object-id> \ --role "Azure Service Bus Data Receiver" \ --scope $QUEUE_ID
Use Managed Identity in .NET (No Connection String Needed)
using Azure.Identity;
using Azure.Messaging.ServiceBus;
// Fully Qualified Namespace (no connection string)
string fqNamespace = "myshopns.servicebus.windows.net";
// DefaultAzureCredential automatically uses Managed Identity in Azure
var credential = new DefaultAzureCredential();
await using var client = new ServiceBusClient(fqNamespace, credential);
await using var sender = client.CreateSender("orders");
await sender.SendMessageAsync(new ServiceBusMessage("RBAC-authenticated order"));
Console.WriteLine("Message sent using Managed Identity. No secrets used.");
Use Managed Identity in Python
from azure.identity import DefaultAzureCredential
from azure.servicebus import ServiceBusClient, ServiceBusMessage
fq_namespace = "myshopns.servicebus.windows.net"
credential = DefaultAzureCredential()
with ServiceBusClient(fq_namespace, credential) as client:
with client.get_queue_sender("orders") as sender:
msg = ServiceBusMessage("RBAC message from Python")
sender.send_messages(msg)
print("Message sent using Managed Identity.")
RBAC vs SAS — Decision Guide
Is the app running inside Azure (App Service, Function, AKS, VM)? YES --> Use Managed Identity + RBAC (no secrets, automatic token rotation) NO --> Use Service Principal + RBAC (with client secret stored in Key Vault) Is the client external (partner, mobile app, IoT device)? YES --> Generate a scoped SAS token with short expiry NO --> Prefer RBAC Is this development or testing? YES --> SAS connection string is acceptable NO --> Always use RBAC in production
Disable Local (SAS) Authentication
For maximum security in production namespaces, disable SAS authentication entirely. This forces all access through Azure Active Directory, eliminating the risk of stolen connection strings.
az servicebus namespace update \ --resource-group rg-messaging-prod \ --name myshopns \ --disable-local-auth true
Summary
Azure Service Bus supports two authentication models. SAS uses cryptographic keys embedded in connection strings or tokens and is straightforward to set up but requires secret management. RBAC uses Azure Active Directory identities and requires no secrets when used with Managed Identity — making it the recommended approach for all Azure-hosted production workloads. SAS policies should be scoped to least privilege at the entity level. In production, store connection strings in Azure Key Vault and prefer Managed Identity to eliminate secret rotation entirely.
