What it is
An Event Hubs namespace that still accepts shared access signature (SAS) key authentication, which is the default on every namespace. SAS keys are long-lived static secrets: the auto-created RootManageSharedAccessKey rule grants manage, send, and listen rights across the entire namespace, and any policy's key authenticates whoever presents it. No Entra ID sign-in, no conditional access, no MFA, and no record of which human or workload was actually behind the connection. Until disableLocalAuth is set to true, the keys are a parallel front door that ignores your identity stack.
Why it happens
SAS keys are the original Event Hubs access model, and they are still what the portal's "Connection string" blade, most SDK samples, and every Kafka-compatibility guide hand you first. For Kafka clients the connection string literally becomes the password, so it gets pasted into producer configs, consumer configs, CI pipelines, and Kubernetes secrets as a matter of course.
disableLocalAuth defaults to false and nothing in the create flow suggests flipping it. Meanwhile the keys spread: one connection string can serve a dozen producers and consumers across teams, and rotating it breaks all of them at once, so rotation is deferred and the same secret keeps working for years.
The fix is two steps: move producers and consumers to Entra ID auth (a managed identity or DefaultAzureCredential plus the Event Hubs Data Sender / Data Receiver roles), then set disableLocalAuth: true so the keys stop authenticating. The second step is the one that actually closes the door.
What it costs / blast radius
A leaked connection string, from a committed config file, a CI log, or a departed contractor's laptop, is full access to the event stream: read live events, replay everything in the retention window, or inject forged events that downstream processors, alerting pipelines, and data lakes ingest as real. Injection is the nasty half; few consumers authenticate individual events, they trust the stream. Containment is blunt too: a key is not an identity, so you cannot revoke one holder, only rotate the key for every consumer at once. Diagnostic logs can show that the namespace was accessed, not by whom. (Authored assessment of Azure behavior, not a measured statistic.)
See it
resource ehns 'Microsoft.EventHub/namespaces@2024-01-01' = {
name: 'ehns-telemetry-prod'
location: location
sku: { name: 'Standard', tier: 'Standard' }
properties: {
// disableLocalAuth omitted — namespace SAS keys grant
// send/listen/manage with no identity behind them
}
}resource ehns 'Microsoft.EventHub/namespaces@2024-01-01' = {
name: 'ehns-telemetry-prod'
location: location
sku: { name: 'Standard', tier: 'Standard' }
properties: {
disableLocalAuth: true // SAS keys no longer authenticate
}
}
// Data-plane access via Azure RBAC instead. App code switches to
// DefaultAzureCredential — no connection string to leak.
resource sendRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(ehns.id, appPrincipalId, 'eh-data-sender')
scope: ehns
properties: {
// Built-in "Azure Event Hubs Data Sender" role
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'2b629674-e913-4c01-ae53-ef4638d8f975')
principalId: appPrincipalId
}
}How StratoLens helps
StratoLens continuously flags every Event Hubs namespace where SAS-key auth is still enabled, across all your subscriptions, inside your own tenant. Rather than trusting that every team remembered disableLocalAuth, you get a standing list of the namespaces where a leaked connection string is still a full credential to the stream.