DEV Community

Cover image for SC-500 deep dive #1: your Contributors can read your Key Vault secrets
CD Security Lab
CD Security Lab

Posted on Originally published at gitlab.com

SC-500 deep dive #1: your Contributors can read your Key Vault secrets

Every Thursday I post SC-500 practice questions. Every Saturday, from this week, I take one exam topic and go deep: the real problem, how it works, the fix, and the exam traps. Week 1: who can really read the secrets in Azure Key Vault.

Lab story, lab names only (contoso.com). Screens are drawings, numbers are invented.

The problem: a "deploy-only" account that read 41 secrets

In my lab, Contoso gave a contractor account, lab.contractor02, the Contributor role on the resource group rg-lab-payments. The reason was simple: "they need to fix a deployment, nothing else". Contributor cannot give roles to anyone, so it felt safe.

Two weeks later a review of the vault kv-lab-pay-01 found something nobody had added:

  • a new access policy for lab.contractor02, with Get and List on secrets
  • created by lab.contractor02 itself
  • 41 secrets readable, including the payment API key and the SQL connection string

No alarm fired. Nothing was "hacked". The vault simply used the old permission model, and in that model, Contributor is enough to give yourself the keys.

Before: the access policy the contractor added for itself

How it works: two permission models, one vault

A key vault has two layers:

Layer What it controls Example
Management plane The vault as an Azure resource create, delete, change network rules, change the permission model
Data plane What is inside read a secret, sign with a key, import a certificate

For the data plane, a vault uses one of two models:

  1. Vault access policy (legacy). The permission list lives inside the vault, as a vault property. Anyone who can write the vault can edit that list. Microsoft's built-in roles page says it plainly: Contributor, Key Vault Contributor, or any role with Microsoft.KeyVault/vaults/write can add an access policy and give themselves data plane access.
  2. Azure RBAC (recommended). Data access is a normal role assignment, such as Key Vault Secrets User. Only roles that can write role assignments can grant it, such as Owner or User Access Administrator. Contributor and Key Vault Contributor manage the vault but cannot read secrets.

So the same role, Contributor, means "manage the vault" in one model and "manage the vault and read every secret" in the other.

The two models side by side

Why it matters right now

Microsoft is making RBAC the default:

  • With Key Vault API version 2026-02-01 or later, a new vault uses Azure RBAC by default. If you still want access policies, you must set enableRbacAuthorization to false when you create it.
  • Existing vaults do not change. A vault created with an older API version keeps using access policies until you switch it.
  • All Key Vault control plane API versions before 2026-02-01 retire on 27 February 2027. Azure CLI 2.90.0 and Azure PowerShell 16.3.0 (or later) support the new version.

The trap: a team creates a new vault with the new CLI, their app gets 403 Forbidden because it has no role, and someone "fixes" it by switching the vault back to access policies. Now the problem from my lab is back.

The fix: find, map, switch, lock

1. Find every vault still on access policies

Azure Resource Graph, across all subscriptions you can see:

resources
| where type == "microsoft.keyvault/vaults"
| extend rbac = tobool(properties.enableRbacAuthorization)
| where isnull(rbac) or rbac == false
| project name, resourceGroup, subscriptionId, policies = array_length(properties.accessPolicies)
Enter fullscreen mode Exit fullscreen mode

In my lab: 9 vaults, 3 still on access policies.

2. List who is in each access policy today

az keyvault show --name kv-lab-pay-01 --query "properties.accessPolicies[].{objectId:objectId, secrets:join(',', permissions.secrets)}" -o table
Enter fullscreen mode Exit fullscreen mode

Remove anyone who should not be there (in my lab: lab.contractor02). Then write down what each real user or app needs.

3. Give the same access with RBAC roles, before you switch

Map each access policy to the smallest built-in role:

Access policy today RBAC role
Secrets: Get, List (an app reads secrets) Key Vault Secrets User
Secrets: all (a team manages secrets) Key Vault Secrets Officer
Keys: Get, Wrap, Unwrap (encryption at rest) Key Vault Crypto Service Encryption User
Everything (vault admins) Key Vault Administrator
Reads names only, not values Key Vault Reader

Assign at the vault scope, not the resource group:

KV_ID=$(az keyvault show --name kv-lab-pay-01 --query id -o tsv)
az role assignment create --role "Key Vault Secrets User" --assignee "$APP_ID" --scope "$KV_ID"
Enter fullscreen mode Exit fullscreen mode

For people, make the role eligible in Privileged Identity Management instead of permanent. PIM works with RBAC roles. It does not work with access policies.

4. Switch the vault to RBAC

az keyvault update --name kv-lab-pay-01 --enable-rbac-authorization true
Enter fullscreen mode Exit fullscreen mode

The moment you switch, all access policies stop working. That is why step 3 comes first. Role assignments can take a few minutes to apply, so switch in a quiet hour and test the app right after.

After: RBAC on, one role per app, contractor gone

5. Lock it so it cannot come back

  • Assign the built-in Azure Policy "Azure Key Vault should use RBAC permission model" at the management group, first in Audit, then Deny.
  • Watch for changes. In Microsoft Sentinel (Azure Activity connector):
AzureActivity
| where OperationNameValue in~ ("MICROSOFT.KEYVAULT/VAULTS/ACCESSPOLICIES/WRITE", "MICROSOFT.KEYVAULT/VAULTS/WRITE")
| project TimeGenerated, Caller, ResourceGroup, _ResourceId, ActivityStatusValue
| order by TimeGenerated desc
Enter fullscreen mode Exit fullscreen mode

An access policy write on any vault after the switch is either a mistake or someone trying the old trick. Both deserve a ticket.

SC-500 exam traps

Key Vault permission questions were a classic in AZ-500 and they are still in scope for SC-500. Watch for these:

  1. "The user has Contributor. Can they read the secret?" The answer depends on the permission model. Access policy vault: they can give themselves access. RBAC vault: no.
  2. "Key Vault Contributor" sounds like data access. It is not. It manages the vault resource only.
  3. "Grant the least privilege for an app that reads one secret." Key Vault Secrets User, scoped to the vault (or to that one secret), not Key Vault Administrator, not Contributor.
  4. "You switch the vault to RBAC. What happens to existing access policies?" They stop applying. Assign roles first.
  5. "Who can grant data access on an RBAC vault?" Roles with role assignment write permission, such as Owner and User Access Administrator. Not Contributor.

Before and after in my lab

Before After
Vaults on access policies 3 of 9 0 of 9
People who could read payment secrets 7 (incl. the contractor) 2 eligible admins + 1 app
Access policy changes alerted No Yes, Sentinel rule
New vaults with access policies Allowed Denied by Azure Policy

Cheat sheet

Coming next

Next deep dive: managed identities, and why "no secrets at all" beats "secrets in a well-locked vault". On Thursday: SC-500 practice questions, as usual.

Sources


Thanks for reading! I'm Cuong, and on CD Security Lab I rebuild real Microsoft security cases in a safe lab and explain them simply. Follow along:

Got a question about your own tenant? Drop it in the comments.

Top comments (0)