Twee live demo’s uit Nashville: hoe ver Conditional Access en restricted AU’s reiken

Sam Kloos
Oct 14, 2026 — Sam Kloos

Op 10 september stond ik op HIP Conf 26 in Nashville met een sessie over een gedeelde Entra ID tenant voor kritieke infrastructuur. Het bijzondere aan die tenant is dat de beheerders er als B2B gast binnenkomen vanuit hun eigen organisatie, ook de mensen met de zwaarste rollen. Dat is een mooi verhaal, maar in deze blog wil ik het vooral hebben over de twee demo’s die ik live liet zien. Daar zit namelijk het meeste in wat je morgen in je eigen tenant kunt toepassen.

En ja, er ging ook iets mis. Daar kom ik op terug.

De grote zaal van HIP Conf 26 in Nashville

Demo 1: bescherm de actie, niet alleen de rol

Het uitgangspunt: een hard delete van een soft deleted object is onomkeerbaar. Dus wil je dat niet iedere beheerder dat zomaar kan. In Entra ID is die hard delete een Protected Action (microsoft.directory/deletedItems/delete). Die hebben we gekoppeld aan authentication context c2, en een Conditional Access policy blokkeert c2 voor iedereen behalve break-glass.

Les 1: een control moet standhouden tegen de persoon in beeld
In eerdere versies van deze demo was ik Global Administrator. Dat klinkt indrukwekkend, maar het bewijst niets: een GA haalt de policy gewoon weg en verwijdert daarna alsnog. In Nashville gebruikte ik daarom een account met User Administrator. Die rol mag een user definitief verwijderen, maar heeft geen enkel recht op Conditional Access. Die persoon kan de control dus niet omzeilen door hem uit te zetten, en dan wordt de demo pas eerlijk.

Het resultaat: de hard delete gaf 401 Authorization_ConditionalAccessRequired. Break-glass deed precies dezelfde call en kreeg 204.

Les 2: een 403 door beperkte tooling is geen control
Daarna probeerde een pipeline service principal met dezelfde rol het, met een gewone client. Dat gaf 403 Authorization_RequestDenied. Mooi, zou je denken. Maar die weigering komt niet door onze policy. De client kan de handshake voor een authentication context gewoon niet doen. Dat is geluk, geen beveiliging.

Les 3: workloads kunnen de claim gewoon zelf aanvragen
Dezelfde service principal vroeg daarna in de token request zelf de c2 claim aan. Resultaat: 204, user weg. Dat was het moment waarop het stil werd in de zaal.

Een Conditional Access policy op een authentication context houdt mensen tegen. Een workload die de claim zelf aanvraagt, komt er zonder problemen langs. Als je dit patroon gebruikt en er draaien service principals in je tenant met rollen die de beschermde actie mogen uitvoeren, dan heb je hetzelfde gat.

De oplossing is een workload identity Conditional Access policy die direct op de service principal gericht is. Dan komt er niet eens een token: AADSTS53003. Die policy vraagt wel een aparte licentie (Workload ID Premium), en het koppelen aan een authentication context liep bij ons via de beta Graph API. Het onderliggende patroon werkt voor mensen gewoon GA: bewaak de actie en sluit de context af. Vergeet alleen de workloads niet.

Demo 2: scope wint van privilege

De klassieke route naar tenant takeover loopt via applicaties. Je krijgt iets in handen met Application Administrator of Application.ReadWrite.All, zet een credential op een privileged app, logt in als die app en je bent binnen.

Wij zetten onze Tier 0 applicaties in een restricted management administrative unit, met Application Administrator gescoped op die AU. In de demo:
  • Een aanvaller met tenant-wide Application Administrator, Application.ReadWrite.All én owner doet addPassword op een app in de AU en krijgt 403 Forbidden.
  • Een operator met Application Administrator op AU scope doet dezelfde call en krijgt 200, met een echte credential in de response (in beeld gemaskeerd).
  • De aanvaller doet dezelfde call op een app buiten de AU en krijgt ook 200.
Les 4: test altijd de controle
Die laatste stap lijkt overbodig, maar is de belangrijkste. Zonder die stap weet je niet of de aanvaller wordt tegengehouden door de AU of door iets anders dat toevallig misgaat. Met die stap weet je dat de muur precies staat waar je denkt.

Les 5: stop zowel de app registration als de service principal in de AU
Dit vonden we tijdens het testen. Als je alleen de app registration of alleen de service principal toevoegt, laat je een deur open. In de ledenlijst van de AU zie je elke app daarom twee keer staan, en dat is precies de bedoeling.

Les 6: ongedocumenteerd is een extra laag, geen fundament
Apps als lid van een AU en Application Administrator op AU scope staan allebei niet in de documentatie van Microsoft, maar ze worden runtime wel afgedwongen. Wij behandelen het als extra laag, met drift detection en een terugkerende test eronder. Als het morgen anders gaat werken, willen we dat merken voordat een aanvaller het merkt.

Intermezzo: de demo die tien seconden voor de start omviel

Beide demo’s draaiden live vanuit de terminal met echte Graph calls. Alles was gescript, er werd niets live getypt of gekopieerd, en secrets werden gemaskeerd. Ze waren tientallen keren getest en hadden nooit gehaperd. Toch had ik een paar uur van tevoren nog een opname gemaakt, voor de zekerheid. Zomaar een ingeving.
Sam achter het spreekgestoelte tijdens de tech check
Je zou zeggen dat dit het moment is waarop het misging. Sorry dat ik het verhaal verpest: dit is de tech check de dag ervoor, en toen werkte alles.

Tien seconden voor de start meldde het script van demo 1 dat het de victim user niet kon aanmaken. Hoe ik daar vervolgens mee worstel voor een volle zaal, kun je rustig terugkijken in de opname van de sessie: Building a Shared Entra ID Tenant for Critical Infrastructure. De sessie is in het Engels. Ik raad het aan, alleen al om mij in gevecht te zien met de demogoden (en om te zien dat ik zo verdomd koppig ben dat ik pas helemaal aan het einde de opname erbij pak).

Uiteindelijk ben ik gewoon doorgegaan met het verhaal. Demo 2 werkte zonder problemen, en demo 1 heb ik aan het einde vanaf de opname laten zien.

De oorzaak was bijna gênant simpel. Demo 1 vernietigt zijn eigen victim, dus het script maakt bij elke run een nieuwe user aan, altijd met dezelfde UPN. Van een eerdere run stond er nog een user in soft delete, en een soft deleted user houdt zijn UPN vast. Een nieuwe user met dezelfde naam aanmaken gaat dan dus niet.

De oplossing is net zo simpel: geef elke run een unieke naam.
stamp=$(date +%Y%m%d%H%M%S)
nick="demo-victim-$stamp"
upn="$nick@<tenant>.onmicrosoft.com"
Dan maakt het niet meer uit wat er nog in de prullenbak ligt.

Tot slot

Wat ik vooral meeneem uit Nashville: de reacties gingen bijna allemaal over de demo’s. Niet omdat ze spectaculair waren, maar omdat ze minder bekende, ongedocumenteerde en preview features combineerden in één echte use case, inclusief de plek waar het misgaat en hoe je dat oplost. Een demo die laat zien waar je control ophoudt, is meer waard dan tien demo’s waarin alles werkt.

En neem je demo op. Ook als hij nog nooit is misgegaan. Juist dan.

Blijf op de hoogte

Schrijf je in voor onze nieuwsbrief en ontvang een seintje wanneer we een nieuw artikel publiceren of nieuws te delen hebben.