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

MethodUses Secret?GranularityRecommended For
SAS (Connection String)Yes — key embedded in stringNamespace or entity levelDev/test, legacy apps, external clients
SAS TokenYes — signed token with expiryNamespace or entity levelShort-lived external access
RBAC + Managed IdentityNo — uses Azure AD identityNamespace, queue, topic, subscriptionProduction Azure services (recommended)
RBAC + Service PrincipalYes — client secret or certNamespace, queue, topic, subscriptionExternal 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

PermissionAllowsUse Case
SendSend messages to queues or topicsProducer applications
ListenReceive messages from queues and subscriptionsConsumer applications
ManageCreate, 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 RootManageSharedAccessKey in 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

RolePermissionsUse Case
Azure Service Bus Data OwnerFull access — Send, Receive, ManageAdmin-level tools, infrastructure deployments
Azure Service Bus Data SenderSend messages onlyProducer services
Azure Service Bus Data ReceiverReceive and settle messages onlyConsumer 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.

Leave a Comment