Aufbau der Zeichenfolge
Das Ja hat drei Teile, durch Punkte getrennt: das Kennzeichen v1, die Mitte und die Signatur. Die Mitte ist ein JSON-Objekt, kodiert in base64url ohne Füllzeichen. Die Signatur ist HMAC-SHA256 über v1. und die Mitte, ebenfalls in base64url.
Die Mitte trägt drei Felder. p bindet das Ja an die Sache: die Kennung der Freigabe (id), die Nummer der Aktion darin (nr) und die Person, die entscheiden darf (wer). exp ist der Ablauf als Sekundenzahl der Unix-Zeit, im Prototyp standardmäßig zehn Minuten nach dem Ausstellen. jti ist ein Einmalwert, eine UUID der Version 4 aus crypto.getRandomValues. Der Schlüssel hat mindestens 32 Byte.
Die Rechnung von SHA-256, HMAC-SHA256 und base64url ist reines TypeScript ohne crypto.subtle, damit sie in jedem Browser läuft, auch ohne verschlüsselte Verbindung. Die Tests rechnen die Ergebnisse mit node:crypto gegen.
v1 . base64url( JSON { p: { id, nr, wer }, exp, jti } ) . base64url( HMAC-SHA256( k, "v1." + Mitte ) )Das Ja im Schnitt
Unten die Zeichenfolge, darüber ihre Mitte. Beim Einlösen wird alles geprüft und der Einmalwert verbraucht; erst dann entsteht eine Zeile im Protokoll. Eine Kopie endet beim Einlösen.
- Protokoll (prev_hash · hash): .
- Einlösen (in fester Folge): Signatur, Ablauf, Bindung, Einmalwert.
- Mitte (p = id · nr · wer): p, exp, jti.
- Zeichenfolge (base64url): v1, Mitte, HMAC-SHA256.
- einlösen: fließt von zeichen nach protokoll.
- Kopie: gesperrt von zeichen nach protokoll.
Prüfung in fester Reihenfolge
Beim Einlösen prüft der Prototyp zuerst die Zeichenfolge selbst: ob überhaupt eine vorliegt, ob sie drei Teile mit v1 vorn hat, ob die Signatur stimmt, ob die Mitte lesbares JSON ist, ob exp und jti vorhanden sind und zuletzt, ob der Ablauf überschritten ist. Die Signatur wird Zeichen für Zeichen ohne frühen Abbruch verglichen, damit die Dauer des Vergleichs nicht von der Stelle der ersten Abweichung abhängt.
Dann die Entscheidung, in dieser Reihenfolge: genau eine Aktion je Entscheidung, keine Sammelfreigabe; eine gültige Wahl aus freigeben, ändern und ablehnen; ein Idempotenzschlüssel gegen den doppelten Klick; eine bekannte Freigabe, deren Frist noch läuft, sonst bleibt nur ablehnen; ein gültiges Ja; die Bindung, bei der Kennung, Aktionsnummer und Person im Ja genau der Anfrage entsprechen müssen; dann der Einmalwert.
Ein abgelaufenes Ja taugt nur noch zum Ablehnen. Jede Abweisung kommt mit Zeit, Kennung, Aktion, Person und Grund in eine Liste der Versuche; die Abweisung selbst schreibt keine Zeile in das Protokoll.
Verbraucht beim Einlösen
Der Einmalwert jti kommt in die Menge der verbrauchten Werte, bevor die Entscheidung wirkt. Wer dieselbe Zeichenfolge mit neuem Idempotenzschlüssel ein zweites Mal vorlegt, etwa als Kopie aus dem Verlauf des Browsers oder aus einer Nachricht, prallt mit dem Grund „schon benutzt“ ab. Mit demselben Schlüssel und demselben Inhalt (Kennung, Aktion, Wahl, Änderung) liefert der Prototyp vor jeder weiteren Prüfung das erste Ergebnis zurück. Eine zweite Wirkung entsteht in keinem Fall. Der Wert bleibt auch dann verbraucht, wenn die Entscheidung danach an einer fehlenden oder bereits entschiedenen Aktion scheitert.
Davon getrennt schützt der Idempotenzschlüssel vor dem doppelten Klick. Kommt dieselbe Anfrage mit demselben Schlüssel und demselben Inhalt noch einmal, liefert der Prototyp das erste Ergebnis zurück, ohne neu zu entscheiden. Derselbe Schlüssel mit anderem Inhalt ist ein Konflikt. Den Inhalt vergleicht er über SHA-256 seines stabilen JSON.
Der Verlauf als Hash-Kette
Jede Zeile des Protokolls hat die Felder seq, zeit, wer, ereignis, id, daten und prev_hash, den Prüfwert der Zeile davor; die erste Zeile beginnt bei 64 Nullen. Ihr eigener Prüfwert hash ist SHA-256 als Hex über prev_hash und das JSON der Zeile ohne hash, mit rekursiv alphabetisch sortierten Schlüsseln.
Die Prüfung rechnet die Kette von vorn nach. Stimmen der Bezug auf den Vorgänger und der neu gerechnete Prüfwert, geht sie weiter. An der ersten Zeile, die nicht passt, hält sie an und meldet deren Nummer. Wer eine Zeile ändert, ohne ihren Prüfwert neu zu rechnen, fällt an dieser Zeile auf; rechnet er ihn neu, an der nächsten. Löschen, Einfügen und Vertauschen in der Mitte fallen ebenso auf.
h₀ = 64 Nullen
hₙ = SHA-256( hₙ₋₁ ‖ JSON_sortiert( Zeileₙ ohne hash ) )
Prüfung: für n = 1, 2, …
Zeileₙ.prev_hash = hₙ₋₁ und Zeileₙ.hash = hₙ, sonst Bruch bei nGrenze der Kette und Verankerung
Eine Kette, die vollständig neu gerechnet wird, ist in sich wieder stimmig. Der Prototyp zeigt das ausdrücklich: Er kann ab einer Zeile alle Prüfwerte neu rechnen, und die Prüfung meldet danach keinen Bruch. Zwei Dinge erkennt die Kette allein nicht: das vollständige Neuschreiben ab einer Zeile und das Abschneiden der letzten Zeilen. Gegen beides hilft nur ein Vergleichswert außerhalb: der verankerte letzte Prüfwert mit seiner Zeilennummer.
Dafür ist vorgesehen, den letzten Prüfwert in festen Abständen außerhalb zu verankern: bei einem Zeitstempeldienst nach RFC 3161, in einem schreibgeschützten Speicher unter fremder Verwaltung oder bei einer dritten Stelle. Wer prüft, vergleicht dann den Stand der Kette mit dem verankerten Wert, und ein neu geschriebener oder gekürzter Verlauf fällt auf. Was nach dem letzten Anker geschrieben wurde, deckt erst der nächste.
Was für den Betrieb dazukommt
Der Prototyp hält Freigaben, verbrauchte Einmalwerte und Protokoll im Speicher eines Browserfensters, und der Schlüssel entsteht für den Besuch. Für den Betrieb vorgesehen sind ein Schlüssel in einem Hardware-Sicherheitsmodul oder Schlüsseldienst mit Rotation, Prüfung und Verbrauch im Server mit einem eindeutigen Index auf dem Einmalwert, damit von zwei gleichzeitigen Einlösungen nur eine gelingt, ein Protokoll in einem Speicher, der nur Anhängen erlaubt, und die Bindung des Ja an die angemeldete Sitzung der Person.
Vorgesehen ist auch, das Ja zusätzlich an einen Hash der Parameter zu binden. Dann gilt es nicht nur für Aktion und Person, sondern für genau die Werte, die die Person gesehen hat. Im Prototyp binden Kennung, Aktionsnummer und Person; die Aktion selbst ist mit der Freigabe angelegt und liegt fest.
Eckdaten
- Format
v1.Mitte.Signatur, base64url ohne Füllzeichen- Mitte
pmitid,nr,wer;expin Unix-Sekunden;jtials UUID v4- Signatur
- HMAC-SHA256 über
v1.und Mitte, Schlüssel ab 32 Byte - Ablauf
- im Prototyp standardmäßig zehn Minuten; abgelaufen nur noch ablehnen
- Vergleich
- Zeichen für Zeichen ohne frühen Abbruch
- Einmalig
jtiverbraucht vor der Wirkung; eine Kopie prallt ab- Doppelklick
- Idempotenzschlüssel, Inhalt über SHA-256 des stabilen JSON verglichen
- Protokoll
hash= SHA-256(prev_hash‖ JSON sortiert), Start bei 64 Nullen, Prüfung bis zur ersten Bruchstelle- Für den Betrieb
- vorgesehen: Verankerung nach RFC 3161, Schlüssel im HSM, Verbrauch im Server, Bindung an einen Hash der Parameter
Weiter
- AXON · TechnikRechteWie AXON vor jedem Werkzeugaufruf im Server prüft, ob ein Agent handeln darf, und warum er nie mehr Recht hat als die Person, die ihn beauftragt.
- AXON · TechnikWerkzeugeWie ein Sprachmodell einen Werkzeugaufruf nur vorschlägt, die Laufzeit Schema und Recht prüft und Eingangstexte als Daten behandelt, nie als Anweisung.
- ProduktAXONAgenten arbeiten Vorgänge ab, mit Rechten und Verlauf
Welche Entscheidung trifft bei Ihnen nur eine berechtigte Person? An ihr zeigen wir die Freigabe im Prototyp.