- Je onderscheidt dienstmacht, een geclaimde identiteit en de vertrouwde sessie.
- Je bewaart de normale eigen-kaarttaak terwijl je toegang tot andermans kaart weigert.
Een gedeelde assistent kan de kaartjes van twee klanten lezen. Als iemand vraagt om het kaartje van een ander, is de macht van de assistent dan voldoende? Nee: de applicatie moet apart weten namens welke opdrachtgever zij handelt.
Een verwarde plaatsvervanger (confused deputy) gebruikt zijn eigen bevoegdheden voor iemand die die toegang niet heeft. We oefenen met fictieve klantkaarten, een dienstbereik en apart vertrouwde sessiegegevens. De tekst CANARY-EIGEN is alleen een herkenbare oefenmarkering.
1. Wie vraagt dit werkelijk?
Vergelijk de assistent met een baliemedewerker die alle klantmappen kan openen. De bezoeker mag een naam op een formulier zetten, maar dat formulier vervangt geen vastgestelde identiteit. Ook een vertrouwd account bij de verkeerde tenant, de afgescheiden klantomgeving, hoort niet vanzelf toegang te krijgen.
De sessie bevat actor en tenant. Een kaart bevat owner en tenant; beide moeten passen. De gegeven helpers controleren de gegevensvorm, terwijl jij de bevoegdheidsvergelijking schrijft.
Hier is C de opdrachtgever, O de eigenaar en T de tenant. A wordt alleen waar als zowel actor als tenant klopt. Deze beslissing controleert de opdrachtgever; de dienst moet daarnaast zelf toegang hebben.
Met de hand hoort de kaart bij Alice in rood. Bobs sessie in blauw past op geen van beide velden. We vragen toch dezelfde kaart op, zodat de controle zichtbaar wordt.
cards = [dict(
id="kaart-a", owner="alice",
tenant="rood", text="CANARY-EIGEN")]
bob = dict(actor="bob", tenant="blauw")
answer = caller_access(
cards, bob, "kaart-a")
print(answer["allowed"])
print(answer["reason"])
# False
# tenant
012. Een geclaimde naam geeft geen toegang
De kwetsbare plaatsvervanger heeft een dienstbereik met kaart-a. Hij kijkt vervolgens naar claimed_actor in het aangeleverde voorstel alsof die naam al is vastgesteld. Bob kan daar Alice invullen zonder dat zijn apart vertrouwde sessie verandert.
G is het dienstbereik en p het gekozen doel. De naïeve regel combineert diensttoegang met de onbewezen actorclaim. Het probleem is dus niet alleen een ongeldige tekstvorm: alle velden kunnen keurig gevormd zijn en toch naar de verkeerde opdrachtgever wijzen.
claim = dict(claimed_actor="alice",
target="kaart-a")
service = ["kaart-a"]
leak = naive_deputy(
cards, service, claim)
print(leak["allowed"])
print(leak["text"])
# True
# CANARY-EIGEN
02De dienst kan meer kaarten lezen dan één gebruiker. Een actornaam in het voorstel is geen bewijs dat die gebruiker de aanvraag doet.
kaart-a
Eigenaar: alice
Tenant: rood
Oefeninhoud: "CANARY-EIGEN-A"
kaart-b
Eigenaar: bob
Tenant: blauw
Oefeninhoud: "CANARY-EIGEN-B"
Vertrouwde sessie: alice / rood. Voorstel claimt bob en vraagt kaart-b.
Recht van de opdrachtgever
Geweigerd
De kaart hoort bij een andere tenant.
Kwetsbare assistent
Toegestaan
De controles staan deze toegang toe.
Teruggegeven tekst: "CANARY-EIGEN-B"
Sessiebewuste assistent
Geweigerd
De claim wijkt af van de vertrouwde actor.
Teruggegeven tekst: None
De verdedigde assistent controleert doel, dienstrecht, sessie-identiteit, tenant en eigenaar. De eerste weigering bepaalt de reden. None betekent geen tekst teruggeven; "" is een toegestane lege kaart.
Alle kaarten, rechten, routes en kosten zijn eigen oefengegevens. Deze oefening voert geen externe tools uit en verstuurt geen gegevens.
3. Bind de actie aan vertrouwde context
De verdedigde plaatsvervanger krijgt ook Bobs sessie. Hij vergelijkt de claim met de vertrouwde actor, controleert de tenant en vergelijkt vervolgens de eigenaar. Geen van die grenzen mag door voorsteltekst worden vervangen.
De toestemming vraagt alle voorwaarden tegelijk. De redenlabels in onze oefening tonen de eerste mislukte controle; een echte applicatie moet apart bepalen welke foutinformatie zij veilig kan onthullen. Je schrijft hier geen productielogin of volledig tenantsysteem.
S betekent diensttoegang, I een passende actorclaim en B de bevoegdheidsbeslissing uit het eerste onderdeel. Het teken ∧ verbindt voorwaarden die allemaal nodig zijn. Vormvalidatie gebeurt al vóór deze beslissingen.
safe = guarded_deputy(
cards, service, bob, claim)
print(safe["allowed"])
print(safe["reason"])
print(safe["text"])
# False
# identity
# None
03Een veelgemaakte fout is alleen de naamcontrole toevoegen en de tenant vergeten. Dezelfde actornaam kan in een andere klantomgeving voorkomen. Een tweede fout is alle tekst weigeren, ook wanneer de eigen normale aanvraag wel klopt.
alice = dict(actor="alice",
tenant="rood")
normal = guarded_deputy(
cards, service, alice, claim)
print(normal["allowed"])
print(normal["text"])
# True
# CANARY-EIGEN
Een onbekend doel en een doel buiten het dienstbereik worden eveneens geweigerd. Bij afwijzing is text altijd None; een toegestane maar lege kaart bevat juist een lege string. Bewaar dat onderscheid, zodat ontbrekende toestemming niet voor lege normale inhoud wordt aangezien.
Wat je nu kunt
- Je scheidt dienstmacht van de bevoegdheid van de opdrachtgever.
- Je behandelt actorclaims als gegevens en bewaart apart vertrouwde sessiecontext.
- Je verdedigt de grens terwijl toegang tot de eigen normale kaart blijft werken.
Hierna tekenen we hoe beïnvloeding, privélezing en een uitgaand kanaal samen één concreet pad kunnen vormen.
Bronnen
Gecontroleerd op 30 oktober 2026: AWS over het confused-deputy-probleem en het binden van de opdrachtgever en OWASP 2026 LLM03 over gebruikerscontext en autorisatie. De oefening implementeert geen AWS/IAM; kaarten, sessies en redenlabels zijn eigen materiaal.