Zum Hauptinhalt springen

Verwendung von Callbacks

Der FIT-Connect Zustelldienst informiert Onlinedienst und Verwaltungssysteme (API-Clients) aktiv über neue Einreichungen oder Statusupdates. Hierzu werden HTTP-Callbacks genutzt, die auch als Webhooks bezeichnet werden. Webhooks ermöglichen es, API-Clients aktiv über diese Ereignisse zu informieren, ohne dass eine regelmäßige Abfrage (Polling) nötig wäre. Technisch werden Webhooks als HTTP-POST-Request realisiert.

Im Folgenden verwenden wir die Begriffe Callback und Webhook synonym.

Callback-URL

API-Clients stellen zum Empfang von Callbacks einen HTTP-Endpunkt bereit, an den der Zustelldienst einen HTTP-POST-Request übermitteln kann. Die URL dieses Callback-Endpunkts bezeichnen wir als Callback-URL (callbackUrl). Sie wird von dem an FIT-Connect angebundenen System festgelegt.

Der Callback-Endpunkt muss von außen über das Internet erreichbar sein, damit der Zustelldienst Callbacks an diesen Endpunkt senden kann. Der Callback-Endpunkt MUSS zwingend über TLS (HTTPS) abgesichert sein und DARF NICHT ohne Verschlüsselung via HTTP erreichbar sein. Der Zustelldienst wird Callbacks nur über eine via HTTPS verschlüsselte Verbindung auslösen.

API-Clients müssen auf eingehende Callbacks mit einer HTTP-Response mit einem Status Code 2xx Success antworten (z.B. 200 OK oder 202 Accepted).

Eine solche Callback-URL kann z.B. wie folgt aussehen:

https://fachverfahren.beispielstadt.example.org/callbacks/fit-connect
Warnung

Da der Callback-Endpunkt öffentlich über das Internet erreichbar ist, MÜSSEN angebundene Systeme prüfen, ob eingehende Callbacks von einem vertrauenswürdigen Zustelldienst stammen. Zu diesem Zweck MÜSSEN die im Abschnitt Prüfung von Callbacks beschriebenen Prüfungen durchgeführt werden.

Konfiguration von Callbacks

Eine Konfiguration von Callbacks ist über das Self-Service-Portal und über die API-Endpunkte PUT /v1/destinations/{destinationId} bzw. PATCH /destinations/{destinationId} möglich. Bei der Konfiguration werden die Callback-URL und ein Callback-Secret vom API-Client festgelegt. Das Callback-Secret dient der Überprüfung der Echtheit (Authentizität) von eingehenden Callbacks (siehe nächster Abschnitt). Das angegebene Callback-Secret kann über die API nur geschrieben und aktualisiert, aber nicht gelesen werden und DARF NICHT an Dritte weitergegeben werden.

Ein sicheres Callback-Secret kann über die folgenden Aufrufe erzeugt werden:

  • Python: python -c 'import secrets; print(secrets.token_urlsafe(32))'
  • Ruby: ruby -rsecurerandom -e 'puts SecureRandom.hex(32)'
  • pwgen: pwgen --secure 64 1

Die Einrichtung von Callbacks im Self-Service-Portal wird im Artikel Zustellpunkt anlegen näher beschrieben.

Konfiguration von Callbacks für Zustellpunkte

Über die API können Callbacks für Zustellpunkte wie folgt konfiguriert werden:

$ SUBMISSION_API=https://submission-api-testing.fit-connect.fitko.dev
$ JWT_TOKEN=...
$ DESTINATION_ID=...
$ CALLBACK_URL=https://fachverfahren.beispielstadt.example.org/callbacks/fit-connect
$ CALLBACK_SECRET=insecure_unsafe_qHScgrg_kP-R31jHUwp3GkVkGJolvBchz65b74Lzue0
$ curl -X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT_TOKEN" \
--data "{ \"callback\": { \"url\": \"$CALLBACK_URL\", \"secret\": \"$CALLBACK_SECRET\" }}" \
"$SUBMISSION_API/v1/destinations/$DESTINATION_ID"

Prüfung von Callbacks

Da der Callback-Endpunkt öffentlich über das Internet erreichbar ist, MÜSSEN angebundene Systeme prüfen, ob eingehende Callbacks von einem vertrauenswürdigen Zustelldienst stammen.

Hierzu enthalten Callbacks einen Message Authentication Code (HMAC) gemäß RFC 2104 auf Basis des angegebenen Callback-Secrets. Ein HMAC kann als „symmetrische Signatur“ verstanden werden und ermöglicht die Prüfung der Herkunft und Integrität eines eingehenden Callbacks.

Der HMAC wird im HTTP-Header callback-authentication übertragen. Um Replay-Angriffe zu vermeiden, enthält er einen aktuellen Timestamp. Dieser Timestamp wird im HTTP-Header callback-timestamp übertragen. Bei der Prüfung der Echtheit des ausgelösten Callbacks MÜSSEN API-Clients prüfen, dass der angegebene Timestamp nicht älter als 5 Minuten ist.

Das folgende Beispiel zeigt die Verwendung der HTTP-Header callback-authentication und callback-timestamp. Zu beachten ist, dass das Feld submissionIds als deprecated markiert wurde und durch das Feld submissions ersetzt werden soll.

POST /callbacks/fit-connect
callback-authentication: 2056b372b5bcec06d8f11ab79b84b42d6cbe1c8e1178cdfa36e4385dcf717758aaa7599f417d9ec3e079087884f4fd59680bf713621383e2d4414ef74fb10df3
callback-timestamp: 1672527599

{
"type":"https://schema.fitko.de/fit-connect/submission-api/callbacks/new-submissions",
"submissionIds":["f39ab143-d91a-474a-b69f-b00f1a1873c2"],
"submissions":[
{
"destinationId":"d12caea8-f372-4eb1-b102-b0a228253a11",
"submissionId":"f39ab143-d91a-474a-b69f-b00f1a1873c2",
"caseId":"9eec7d3e-dc66-4f82-9f52-1520bf96a32e"
}
]
}

Der HMAC wird gebildet aus dem im HTTP-Header callback-timestamp übertragenen Zeitstempel und dem im HTTP-Body übertragenen Payload, getrennt durch das Zeichen . (Punkt), jeweils UTF-8-kodiert. Der HMAC wird hexadezimal kodiert übertragen. Als Hash-Algorithmus wird SHA-512 verwendet.

callback-authentication = HEX(HMAC(key={callback-secret}, message={timestamp}.{http-body}))

Um den HMAC zu verifizieren, bildet der API-Client mithilfe des Callback Secret den HMAC nach und vergleicht diesen mit dem im HTTP-Header callback-authentication übertragenen HMAC.

Bei der Prüfung MÜSSEN die folgenden Implementierungshinweise zwingend beachtet werden:

  • Das Callback Secret MUSS in API-Clients konfigurierbar sein und DARF NICHT fest im Quellcode eines API-Clients einprogrammiert sein.
    • Dies kann beispielsweise durch die Konfiguration des Callback Secret in einer Konfigurationsdatei oder über eine Umgebungsvariable ($ export CALLBACK_SECRET=your_secret) erreicht werden.
  • Bei der Erzeugung des HMAC MUSS der Hash-Algorithmus SHA-512 verwendet werden.
  • Es MUSS geprüft werden, dass der angegebene Zeitstempel nicht älter als 5 Minuten ist.
  • Beim Vergleich des übertragenen HMAC und des vom API-Client gebildeten HMAC MUSS ein zeitlich konstanter Zeichenfolgenvergleich (constant time string comparison) verwendet werden.
    • In Python kann dies über die Verwendung der Methode hmac.compare_digest erreicht werden.
    • In Ruby kann dies über die Verwendung der Methode secure_compare erreicht werden.
  • Callbacks mit ungültigem HMAC MÜSSEN von API-Clients ignoriert werden.

Dabei ist zunächst

  • der Zeitstempel (callback-timestamp-Header) und anschließend
  • der HMAC (callback-authentication-Header) zu prüfen:
# 1. Timestamp überprüfen
current_time_epoch = int(time.time())
seconds_five_minutes = 60 * 5
if current_time_epoch - request['headers']['callback-timestamp'] > seconds_five_minutes:
print('Error: timestamp too old')
sys.exit(1)
else:
print('timestamp ok')

# 2. HMAC berechnen
payload = str(request['headers']['callback-timestamp']) + '.' + request['body']

expected_hmac = hmac.digest(CALLBACK_SECRET.encode("utf-8"), payload.encode("utf-8"), digest=sha512)
expected_hmac_hex = binascii.hexlify(expected_hmac).decode('utf-8')

print('hmac', expected_hmac_hex)

# 3. Berechneten HMAC mit HMAC aus HTTP-Header vergleichen
if not hmac.compare_digest(request['headers']['callback-authentication'], expected_hmac_hex):
print('Error: invalid hmac')
sys.exit(2)
else:
print('hmac ok')

Das vollständige Script findet sich auch zur freien Verwendung im FIT-Connect-Tools-Repository.