Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Schluss mit dem Kampf gegen komplexe Berechtigungen: Frischer Wind für Ihren Code

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

By Dima KramskoyAug 5, 202612 min read
Dima Kramskoy

About Dima Kramskoy

Solutions architect who takes ideas from zero to proven. With over 20 years of software engineering, I build PoCs and MVPs on AWS that prove the path before you commit — spanning GenAI, DevOps, and FinOps. Off the clock: live-fire cooking, fishing, the outdoors, and applying Extreme Ownership to everything.

My personal page

PerfectScale™ for Kubernetes

Ready to optimize?

Get your free Kubernetes savings analysis

Überblick

Dieser Beitrag stellt Amazon Verified Permissions (AVP) als praktische Lösung für die Verwaltung komplexer Anwendungsautorisierung vor – mit einem Praxis-Walkthrough anhand des AWS-PetStore-Beispiels.

Das Problem

Kennen Sie das? Sie versuchen, alle möglichen Autorisierungsrollen, Nutzer und Akteure Ihres eigenen SaaS-Produkts unter einen Hut zu bringen – und die Komplexität wächst Ihnen über den Kopf. Ihnen ist der Schmerz vertraut, wenn sich Business-Anforderungen ändern und Sie die passenden Security-Prinzipien im Blick behalten müssen, während Sie gleichzeitig Code-Reviews und A/B-Tests zu den anstehenden Änderungen jonglieren? Und ja, Ihr SaaS läuft in der AWS-Cloud.

Dann ist es Zeit, tief durchzuatmen und einen Schritt zurückzutreten!! Denken Sie über Ihre Lösung nach – und dann noch einmal. Denn ich kann Ihnen vielleicht eine vergleichsweise einfache zeigen! Eine, die Ihnen hilft, Komplexität in den Griff zu bekommen und Ihre Ressourcen und Energie in das zu stecken, was wirklich zählt: Ihr Business und dessen Anforderungen!!

Diese Lösung heißt Amazon Verified Permissions.


Wie Autorisierung in Webanwendungen üblicherweise gehandhabt wird

Schauen wir uns das Autorisierungssystem einer Webanwendung genauer an. Der Bedarf an einem solchen System entsteht mit der Weiterentwicklung der Anwendung. Sie können es im Voraus planen (was als Teil Ihres Systemdesigns die bessere Variante ist), aber Änderungen aufgrund sich wandelnder Business-Anforderungen kommen trotzdem auf Sie zu. Sie werden verschiedene Teile Ihres Systems anpassen müssen, nachdem es bereits in Produktion ist.

Warum braucht es so ein System überhaupt? Aus vielen Gründen:

  • Bestimmte Rollen sollen nur bestimmte Bereiche Ihrer Anwendung sehen dürfen
  • Bestimmte Aktionen müssen für einzelne Rollen/Nutzer Ihrer Anwendung eingeschränkt werden
  • Sie müssen Sicherheitskontrollen verschärfen und unbefugten Zugriff verhindern
  • Ihre Berechtigungslogik verteilt sich über die gesamte Codebasis und macht die Wartung zum Albtraum

Das typische Berechtigungssystem: ein heilloses Durcheinander

Die meisten Autorisierungssysteme sehen heute ungefähr so aus – verstreute if/else-Blöcke im gesamten Code:

// The typical nightmare in your Lambda or Node.js service:
boolean userAuthorizedForOrder(Order order, User user) {
if (user.storeId == order.storeId) {
if (user.roles.contains("store-owner")) {
return true; // store owners can access their own store's orders
} else if (user.roles.contains("customer")) {
if (user.id == order.customerId) {
return true; // customers can only see their own orders
}
}
}
return false;
}

Und dann? Sie fügen eine weitere Rolle hinzu. Dann noch eine. Dann einen Sonderfall für Regionalmanager. Dann eine Regel, wer Bestellungen nach 24 Stunden stornieren darf. Dann eine neue Business-Anforderung zum Wochenendzugriff. Plötzlich ertrinken Sie in verschachtelten Bedingungen, Ihre Code-Reviews werden zur Qual, jede Änderung der Business-Anforderungen bedeutet ein Deployment, und das halbe Team streitet über Edge Cases, statt Features zu bauen.

Wo es wirklich wehtut

Rollen-Explosion: Aus 2 Rollen werden 5, dann 10, und irgendwann gibt es den Regionalmanager-mit-Filialmitarbeiter-Rechten-aber-nur-am-Wochenende – und niemand blickt mehr durch, wer was haben sollte.

Code-Review-Albtraum: Jede Berechtigungsänderung erfordert Codeänderungen. Entwickler diskutieren stundenlang über Policy-Logik in PRs, statt Features auszuliefern.

Autorisierung testen: Wie testet man überhaupt alle Kombinationen? Matrix aus Nutzern × Rollen × Ressourcen × Aktionen = Chaos. Edge Cases werden übersehen, in Produktion geht etwas kaputt.

Audit-Trail: Viel Glück dabei, der Compliance zu erklären, warum dieser eine Nutzer auf jene Bestellung zugreifen konnte. Das steckt irgendwo in der Git-Historie, verteilt über 5 verschiedene Dateien.

Null Flexibilität: Sie wollen Berechtigungen an einem Freitag ändern? Pech gehabt – das bedeutet ein Deployment. Sie wollen eine Policy schnell zurückrollen? Wieder deployen.


So gelingt es mit Amazon Verified Permissions

Was wäre, wenn Sie diese Last vereinfachen könnten? Was, wenn ich Ihnen sage, dass es inzwischen einen Service gibt, der Ihnen den Großteil der Arbeit rund um Berechtigungen in Webanwendungen abnimmt?

Statt die Autorisierung fest in Ihren Code einzubacken – was wäre, wenn Sie sie vollständig auslagern? Was, wenn Berechtigungen deklarativ, versioniert und zentralisiert wären und Sie sie ohne erneutes Deployment Ihrer Anwendung ändern könnten?

Genau das verspricht Amazon Verified Permissions (AVP).

Die Architektur

So funktioniert es im Überblick:

  1. Der Nutzer authentifiziert sich über AWS Cognito (oder einen beliebigen IdP)
  2. Ihre API empfängt den Request mit einem JWT-Token
  3. Der Lambda Authorizer fängt den Request ab
  4. Lambda baut eine Autorisierungsanfrage mit: Principal (wer), Action (was getan wird), Resource (womit), Context (zusätzliche Attribute)
  5. Lambda ruft AVP mit dieser Anfrage auf
  6. AVP wertet Cedar-Policies aus (eine speziell für Autorisierung entwickelte Policy-Sprache)
  7. AVP liefert ALLOW oder DENY zurück
  8. API Gateway setzt die Entscheidung durch – der Request geht weiter oder erhält 403

Die zentrale Erkenntnis: Die gesamte Berechtigungslogik lebt in Cedar-Policies in AVP. Ihr Code fragt nur "Ist das autorisiert?" und vertraut der Antwort.

media

Das Praxisbeispiel: PetStore

Als Beispielanwendung habe ich das PetStore-Repository von AWS verwendet, inklusive der meisten beschriebenen Features – allerdings mit kleinen Anpassungen, da das mitgelieferte Schema nicht funktionierte. Vermutlich liegt das an Updates, die AWS am Service vorgenommen hat. Bedenken Sie: Es ist ein relativ neuer Service, und ich gehe davon aus, dass er in vielen Bereichen noch in Arbeit ist.

Folgen Sie einfach den angegebenen Schritten und beginnen Sie mit dem Deployment der Amplify-Anwendung. Möglicherweise müssen Sie sich mit Ihrem GitHub-Konto identifizieren, um das Repo zu ziehen und in Ihr AWS-Ökosystem zu deployen. Nach dem Deployment lassen sich anhand des Architekturdiagramms oben die Komponenten und ihre Kommunikation untereinander nachvollziehen.


Die Einrichtung – Schritt für Schritt

Schritt 1: Cognito-Nutzer und -Gruppen

Als Identity Provider nutzen wir AWS Cognito. Nach dem Deployment öffnen Sie Ihr Amazon-Cognito-Dashboard und finden dort den User Pool, dessen Name mit "petstoresample" beginnt. Das ist Ihr Pool – hier legen Sie die Nutzer und Gruppen für diese Anwendung an.

Legen Sie drei Nutzer an:

Für diesen PoC brauchen wir drei Nutzer. Einer muss zwingend "abhi" heißen, da dieser Name in der Lambda-Funktion hartkodiert ist. Die anderen beiden sind flexibler – ich habe sie "owner" und "visitor" genannt. Beim Anlegen gilt:

  • Keine E-Mail-Verifizierungen versenden
  • Die Nutzer als "email address verified" markieren
  • Einfache Passwörter verwenden (Sie wollen sie nach dem Setup nicht zurücksetzen müssen)

Am Ende sollte es ungefähr so aussehen:

media

Fügen Sie dem Nutzer owner benutzerdefinierte Attribute hinzu:

Vergessen Sie nicht, den Nutzer "owner" mit dem benutzerdefinierten Attribut employmentStoreCode zu aktualisieren. Dieses wird später in den Policies von Amazon Verified Permissions verwendet. Klicken Sie einfach auf "Edit" und passen Sie die Attribute so an, dass sie folgendermaßen aussehen:

media

Gruppen anlegen und Nutzer zuweisen:

Sie müssen 2 Gruppen anlegen und die Nutzer diesen Gruppen zuordnen. Diese Gruppennamen ("Store-Owner-Role" und "Customer") sind Teil der von Cognito ausgestellten Tokens und werden für die rollenbasierte Zugriffskontrolle (RBAC) verwendet.

media

Der Nutzer "abhi" muss zusammen mit dem Nutzer "visitor" in die Gruppe "Customer" aufgenommen werden:

media

Und der "owner" mit dem zusätzlich hinzugefügten Attribut kommt natürlich in die Gruppe "Store-Owner-Role":

media

Schritt 2: Amazon Verified Permissions einrichten

Dieser Teil ist abgeschlossen. Als Nächstes prüfen und konfigurieren wir Amazon Verified Permissions. Dabei schauen wir uns auch den Lambda Authorizer und das API Gateway an – beide wurden im Rahmen desselben Amplify-Deployments als Teil der Anwendung bereitgestellt.

Navigieren Sie einfach über das Suchfeld zu Amazon Verified Permissions und legen Sie zunächst einen neuen Policy Store an. Im Repository wird empfohlen, mit dem Schema zu starten, aber die UI hat sich geändert – Sie müssen zuerst den Policy Store erstellen. Die Angaben dabei sind zweitrangig, denn am Ende verwenden wir ohnehin das Schema, das ich hier bereitstelle. Wählen Sie der Einfachheit halber einen leeren Policy Store und geben Sie ihm den Namen "PetStore".

Schritt 3: Das Schema definieren

Dieser Schritt ist entscheidend. Das Schema teilt AVP mit, welche Principals, Ressourcen, Aktionen und Beziehungen in Ihrer Anwendung existieren. Verwenden Sie dieses Schema:

{
"MyApplication": {
"actions": {
"GetOrder": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["Order"]
}
},
"GetStoreInventory": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["Application"]
}
},
"ListOrders": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["Application"]
}
},
"PlaceOrder": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["Application"]
}
},
"SearchPets": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["Application"]
}
}
},
"entityTypes": {
"Application": {
"memberOfTypes": [],
"shape": {
"attributes": {
"storeId": {"type": "String"}
},
"type": "Record"
}
},
"Order": {
"memberOfTypes": [],
"shape": {
"attributes": {
"owner": {
"name": "User",
"type": "Entity"
},
"storeId": {
"type": "String"
}
},
"type": "Record"
}
},
"Pet": {
"memberOfTypes": [],
"shape": {
"attributes": {
"owner": {
"name": "User",
"type": "Entity"
},
"storeId": {
"type": "String"
}
},
"type": "Record"
}
},
"Group": {
"memberOfTypes": [],
"shape": {
"attributes": {},
"type": "Record"
}
},
"User": {
"memberOfTypes": ["Group"],
"shape": {
"attributes": {
"employmentStoreCode": {"type": "String"}
},
"type": "Record"
}
}
}
}
}

Das Schema im Originalprojekt hat einen kleinen Fehler – kopieren Sie daher stattdessen dieses Schema in Ihren Policy Store.

Schritt 4: Cedar-Policies erstellen

Klicken Sie auf den Policy Store; im linken Bereich finden Sie den Link "Policies". Klicken Sie darauf und anschließend auf "Create Policy > Create static policy". Der Assistent zur Policy-Erstellung nutzt das zuvor hinzugefügte Schema. Wir arbeiten mit permissiven Policies – der Geltungsbereich sollte auf die Gruppe der Beteiligten festgelegt werden, der Ressourcen-Scope muss die konkrete Ressource sein, bei der Sie auswählen, was Sie benötigen, und als Aktionstyp muss ein spezifisches Set von Aktionen ausgewählt werden.

Ein kleiner Trick: Erstellen Sie zunächst eine vollständig permissive Policy mit "All principals", "All resources" und "All actions", speichern Sie sie, bearbeiten Sie sie anschließend und fügen Sie die Policy einfach in Cedar-Syntax ein. Das ist ein Workaround – Sie müssen Ihre Policy-Deklaration allerdings kennen.

Hier die Policies, die Sie für diesen PoC benötigen:

RBAC-Beispiel – Kunden dürfen Haustiere suchen und Bestellungen aufgeben:

permit(
principal in MyApplication::Group::"Customer",
action in [MyApplication::Action::"SearchPets", MyApplication::Action::"PlaceOrder"],
resource == MyApplication::Application::"main"
);

ABAC-Beispiel – Store-Besitzer dürfen nur auf das Inventar ihres eigenen Stores zugreifen:

permit(
principal in MyApplication::Group::"Store-Owner-Role",
action == MyApplication::Action::"GetStoreInventory",
resource == MyApplication::Application::"main"
)
when {
principal.employmentStoreCode == resource.storeId
};

ABAC-Beispiel – Nutzer dürfen nur ihre eigenen Bestellungen einsehen:

permit(
principal in MyApplication::Group::"Customer",
action == MyApplication::Action::"GetOrder",
resource is MyApplication::Order
)
when {
principal == resource.owner
};

Cloud bill shouldn't be a mystery

One platform for AI and Cloud optimization.

Die Lambda-Authorizer-Integration

Ihre Lambda-Funktion empfängt den API-Request und muss:

  1. Das JWT-Token verifizieren (Nutzerinformationen extrahieren)
  2. Eine Autorisierungsanfrage für AVP aufbauen
  3. Die isAuthorized-API von AVP aufrufen
  4. ALLOW/DENY zurückgeben

So sieht das aus (aus dem tatsächlichen PetStore-Repo):

const AWS = require('aws-sdk');
const avp = new AWS.Service({
apiConfig: require('./verifiedpermissions.json')
});
const { CognitoJwtVerifier } = require("aws-jwt-verify");
const jwtVerifier = CognitoJwtVerifier.create({
userPoolId: process.env.USERPOOLID,
tokenUse: "id",
clientId: process.env.APPCLIENTID
});
const policyStoreId = process.env.POLICYSTOREID;
exports.handler = async (event) => {
// Extract and verify the JWT token
const token = event.headers["Authorization"];
let payload;
try {
payload = await jwtVerifier.verify(token);
var groups = payload["cognito:groups"] || [];
// Build the principal entity (the user)
var userEntity = {
"identifier": {
"entityType": "MyApplication::User",
"entityId": payload["cognito:username"]
},
"attributes": {
"employmentStoreCode": {
"string": payload["custom:employmentStoreCode"] == null ? "" : payload["custom:employmentStoreCode"]
}
},
"parents": []
};
// Add groups as parent entities (for group membership)
groups.forEach((group) => {
userEntity.parents.push({
"entityType": "MyApplication::Group",
"entityId": group
});
});
// Build the authorization query
let entities = {
entityList: [userEntity]
};
// Add resource entities based on the action
addResourceEntities(entities, actionMap[event.httpMethod + event.resource], event.pathParameters);
// Call AVP
let authQuery = {
policyStoreId,
principal: { "entityType": "MyApplication::User", "entityId": payload["cognito:username"] },
action: { "actionType": "MyApplication::Action", "actionId": actionMap[event.httpMethod + event.resource] },
resource: buildResource(actionMap[event.httpMethod + event.resource], event.pathParameters),
entities
};
const authResult = await avp.isAuthorized(authQuery).promise();
if (authResult.decision == 'ALLOW') {
return buildResponse(200, 'Authorized', authResult);
} else {
return buildResponse(403, "You are not authorized to perform this action.", authResult);
}
} catch(err) {
console.log(err);
return buildResponse(403, "Error: " + err);
}
};

Das Wichtigste: Ihr Code enthält keinerlei Berechtigungslogik. Er übergibt lediglich den Kontext an AVP und vertraut der Antwort. Genau darum geht es.


Der eigentliche Vorteil – Änderungen ohne Redeployment

Und hier passiert die eigentliche Magie.

Szenario: Ihr Business sagt: "Ab nächstem Monat dürfen Nutzer mit der Store-Owner-Role auch Bestellungen stornieren – aber nur solche, die vor mehr als 24 Stunden aufgegeben wurden."

Mit klassischen, hartkodierten Berechtigungen:

  • Sie ändern den Lambda-Code
  • Sie fügen eine getOrderAge()-Funktion hinzu
  • Sie schreiben Tests für die neue Logik
  • Sie committen, erstellen einen PR und warten auf das Code-Review
  • Jemand stellt Ihre Logik infrage, Sie iterieren
  • Sie mergen, deployen auf Staging, testen
  • Sie deployen in Produktion
  • Gibt es einen Bug, deployen Sie erneut – während das Business Geld verliert

Mit Amazon Verified Permissions:

  • Sie loggen sich in die AVP-Konsole ein
  • Sie fügen eine neue Cedar-Policy hinzu (dauert 2 Minuten):
permit(
principal in MyApplication::Group::"Store-Owner-Role",
action == MyApplication::Action::"CancelOrder",
resource is MyApplication::Order
)
when {
principal.employmentStoreCode == resource.storeId &&
resource.age > 1440 // minutes
};
  • Auf Speichern klicken
  • Fertig – die Änderung ist sofort live. Kein Deployment. Keine Codeänderung. Kein Lambda-Redeploy.

Das war's. Ihr Code hat sich nicht geändert. Ihr Lambda wurde nicht neu deployt. Die Autorisierungsregel wurde in Echtzeit aktualisiert.


RBAC + ABAC: Das Beste aus beiden Welten

Cedar erlaubt die Kombination beider Ansätze:

RBAC allein: "Ist der Nutzer in der Admin-Gruppe?" (binär, berücksichtigt keine Ressourcenattribute)

ABAC allein: "Stimmt das department-Attribut des Nutzers mit dem department-Attribut der Ressource überein?" (feingranular, erfordert aber viele Attribute)

RBAC + ABAC (die Stärke von Cedar): "Ist der Nutzer in der Gruppe Store-Owner-Role UND stimmt sein employmentStoreCode mit der storeId der Ressource überein?"

permit(
principal in MyApplication::Group::"Store-Owner-Role",
action == MyApplication::Action::"ManageInventory",
resource is MyApplication::Application
)
when {
principal.employmentStoreCode == resource.storeId
};

So erhalten Sie das Beste aus beiden Welten: rollenbasierte Gruppierung plus attributbasierte Granularität.


Testen & Deployment

Sobald Ihre Policies stehen, testen Sie sie:

  1. Als Customer-Nutzer: Funktioniert SearchPets? ✅ Ja. Funktioniert GetStoreInventory? ❌ Nein.
  2. Als Store-Owner-Role-Nutzer: Funktioniert GetStoreInventory für den eigenen Store? ✅ Ja. Für einen anderen Store? ❌ Nein.
  3. Als owner mit employmentStoreCode=petstore-london: Ist der Zugriff auf Bestellungen von petstore-london möglich? ✅ Ja.

Wenn eine Policy nicht funktioniert, prüfen Sie:

  • Sind Ihre Entitäten in der Lambda-Funktion korrekt strukturiert?
  • Enthält das JWT des Nutzers die richtigen cognito:groups?
  • Ist das benutzerdefinierte Attribut im Nutzerprofil hinterlegt?
  • Stimmen die Entitätstypen in Ihrer Autorisierungsanfrage mit Ihrem Schema überein?

Fazit: Weniger Klempnerarbeit, mehr Wertschöpfung

Ich wollte mit diesem Beitrag Amazon Verified Permissions nicht als ultimative Wunderwaffe für Ihre Anwendung präsentieren. Aber wenn Sie sich in einem endlosen Prozess wiederfinden, in dem Sie den Code Ihrer rollenbasierten Webanwendung ständig anpassen, weil sich die Business-Anforderungen so häufig ändern, lohnt es sich, kurz innezuhalten, einen Schritt zurückzutreten und zu hinterfragen, was Sie da eigentlich tun.

Vielleicht hilft Ihnen dieser Service, Zeit und Ressourcen zu sparen und sich auf das Business statt auf die Klempnerarbeit zu konzentrieren. Denn am Ende ist das alles Klempnerarbeit – sie unterstützt zwar die Geschäftsziele Ihrer Anwendung, steigert aber nicht wirklich deren Wert.

Aber mit Amazon Verified Permissions reparieren Sie zumindest nicht jede Woche die Rohre.