Zurück zum Blog SMS-Betrieb

Parallelität beim SMS-OTP-Versand: Status, Sperren und erneute Versandversuche

Ein praktischer Leitfaden zur Koordination gleichzeitiger OTP-Anfragen, zur Verringerung von Duplikaten und zum vorsichtigen Umgang mit Timeouts, bei denen unklar ist, ob der Versand erfolgt ist.

Diagramm zu SMS-OTP-Anfragen, die über Status und eine temporäre Sperre koordiniert werden

Das Problem: mehrere Anfragen für eine einzige Verifizierung

Ein wiederholtes Antippen, zwei geöffnete Tabs oder mehrere Anfragen, die nahezu gleichzeitig eintreffen, können mehr als einen OTP-Ablauf auslösen. Ebenso können zwei Worker gleichzeitig gleichwertige Anfragen verarbeiten. Dadurch können unterschiedliche Codes erzeugt, mehrere Versandversuche gestartet oder Nutzer mit einem Code zurückgelassen werden, der nicht mehr zum Status der Anwendung passt.

Anfragelimits und temporäre Sperren können helfen, die Häufigkeit zu kontrollieren. Für sich genommen verhindern sie jedoch nicht, dass zwei gleichzeitig laufende Prozesse denselben Vorgang beanspruchen. Die Koordination muss daher auch im gemeinsam genutzten Speicher oder Mechanismus der Anwendung erfolgen.

  • Prüfe, ob gleichzeitige Anfragen zur selben Verifizierung gehören – nicht nur, ob sie von derselben Telefonnummer stammen.
  • Unterscheide den Schutz vor zu vielen Anfragen von der Vermeidung doppelter Verarbeitung.
  • Werte das Ausbleiben einer sofortigen Antwort nicht als Beweis dafür, dass der Versand nicht gestartet wurde.
Das Problem: mehrere Anfragen für eine einzige Verifizierung

Verifizierung, Code und Versandversuch getrennt behandeln

Modelliere drei Elemente getrennt voneinander: die Authentifizierungsanfrage, das zugehörige OTP und jeden einzelnen Versandversuch der Nachricht. So lässt sich eine neue Verifizierung von einem erneuten Versand unterscheiden, und der Verlauf bleibt nachvollziehbar, auch wenn sich der Status des Codes ändert.

Lege zunächst fest, was in deinem Produkt als „derselbe Vorgang“ gilt. Eine wiederholte Anfrage für eine aktive Verifizierung kann anders behandelt werden müssen als eine neue Verifizierung, die gestartet wird, nachdem die vorherige abgeschlossen, abgebrochen oder abgelaufen ist.

  • Ordne jeder Verifizierung eine stabile interne ID zu.
  • Protokolliere jeden Versandversuch mit einer eigenen ID und einem Verweis auf die Verifizierung.
  • Verwende die Telefonnummer nicht als einziges Kriterium zur Deduplizierung: Ein Nutzer kann berechtigterweise mehrere Verifizierungen für unterschiedliche Zwecke oder Sitzungen starten.
Verifizierung, Code und Versandversuch getrennt behandeln

Einen Operationsschlüssel mit eindeutigem Gültigkeitsbereich festlegen

Ein Operationsschlüssel sorgt dafür, dass gleichwertige Anfragen im selben Kontrolldatensatz zusammenlaufen. Er kann sich aus einer Benutzer- oder Sitzungs-ID, dem Authentifizierungszweck und einem durch die Servicerichtlinie festgelegten Zeitfenster zusammensetzen. Die konkrete Wahl hängt davon ab, wie die Anwendung Sitzungen und Verifizierungen abgrenzt; eine universell passende Kombination gibt es nicht.

Das Zeitfenster sollte Wiederholungen eines Vorgangs gruppieren, nicht aber neue Verifizierungen dauerhaft blockieren. Halte den Deduplizierungsschlüssel und die Regeln für Anfragelimits oder temporäre Sperren getrennt: Sie verfolgen verwandte, aber unterschiedliche Ziele.

  • Lege fest, welche Elemente zwei Anfragen gleichwertig machen.
  • Verwende ein klar definiertes Zeitfenster und dokumentiere, wann ein neuer Vorgang beginnt.
  • Vermeide unnötige sensible Daten in sichtbaren Schlüsseln oder Betriebsprotokollen.

Explizite Statuswerte und eine atomare Beanspruchung verwenden

Bilde den Ablauf mit verständlichen Statuswerten ab, zum Beispiel: angefordert, in Bearbeitung, vom Anbieter angenommen, Ergebnis ungewiss, abgeschlossen, fehlgeschlagen oder abgelaufen. Passe Bezeichnungen und Übergänge an die Informationen an, die dein System und der Anbieter tatsächlich zurückgeben. Markiere einen Versand nicht allein deshalb als angenommen, weil intern eine Aufgabe angelegt wurde.

Der Übergang, mit dem ein Worker einen Vorgang beansprucht, muss atomar sein. Praktisch versucht ein Worker, den Datensatz von einem beanspruchbaren Status auf „in Bearbeitung“ zu setzen, und fährt nur fort, wenn der Übergang erfolgreich war. Hat ein anderer Worker den Vorgang bereits beansprucht, sollte der zweite den vorhandenen Status abfragen und der festgelegten Richtlinie folgen – standardmäßig aber keinen weiteren Versand starten.

Die Atomarität hängt vom verwendeten Koordinationsmechanismus ab. Eine Sperre im Arbeitsspeicher eines einzelnen Prozesses koordiniert nicht automatisch mehrere Anwendungsinstanzen. Prüfe, ob die gewählte Steuerung für alle Worker gilt, die denselben Vorgang erhalten können.

  • Lege Statuswerte und zulässige Übergänge fest, bevor du Wiederholungsversuche implementierst.
  • Führe das Beanspruchen des Vorgangs und die Statusänderung als unteilbare Operation im gemeinsam genutzten Mechanismus aus.
  • Lege fest, welche Antwort eine wiederholte Anfrage erhält, wenn der Vorgang bereits läuft: warten, den Status abfragen oder eine Rückmeldung wie „in Bearbeitung“ erhalten.
  • Sorge für eine kontrollierte Möglichkeit, verwaiste Vorgänge wieder aufzunehmen, ohne jeden älteren Vorgang automatisch erneut zu versenden.

Festlegen, wann der Code wiederverwendet und wann ein neuer erstellt wird

Eine doppelte Anfrage während einer noch gültigen Verifizierung kann als Abfrage oder Wiederholung desselben Vorgangs behandelt werden. Wenn Sicherheitsrichtlinie und Nutzererlebnis es erlauben, kann die Anwendung das gültige OTP beibehalten und auf die Erstellung eines weiteren Codes verzichten. Diese Entscheidung muss mit der Gültigkeitsdauer und den Regeln zur Ungültigmachung im System abgestimmt sein.

Ein neuer Code kann sinnvoll sein, wenn eine neue Verifizierung beginnt, der vorherige Code nicht mehr gültig ist oder eine dokumentierte Richtlinie seinen Ersatz verlangt. Lege in diesem Fall fest, was mit älteren Codes geschieht, und stelle sicher, dass die Validierung diese Regel einheitlich umsetzt. Eine für alle Anwendungen passende Richtlinie gibt es nicht.

  • Dokumentiere, welches Ereignis eine neue Verifizierung auslöst und was lediglich eine Wiederholung der Anfrage ist.
  • Lege fest, wie ein noch gültiges OTP bei einer wiederholten Anfrage behandelt wird.
  • Wenn ein neuer Code ausgegeben wird, bestimme ausdrücklich, ob der vorherige gültig bleibt oder ungültig wird.
  • Stimme die Rückmeldung der Benutzeroberfläche auf den tatsächlichen Status ab: Behaupte nicht, ein neuer Code sei versendet worden, wenn die Anwendung keinen neuen Versandversuch gestartet hat.

Timeouts als ungewisses Ergebnis behandeln

Wird die Verbindung unterbrochen oder läuft die Wartezeit ab, nachdem eine Anfrage an den Anbieter gestartet wurde, lässt sich möglicherweise nicht feststellen, ob der Anbieter sie erhalten oder angenommen hat. Ein Timeout beschreibt, was deine Anwendung beobachtet hat; es beweist nicht, dass die SMS nicht versendet wurde.

Protokolliere den Versuch als ungewiss, wenn du das Ergebnis nicht bestätigen kannst. Prüfe vor einer Wiederholung, ob die Versand-API oder -Verbindung einen Idempotenzschlüssel, eine Statusabfrage oder einen anderen dokumentierten Mechanismus zur Erkennung derselben Anfrage bietet. Gibt es keine verlässliche Möglichkeit, den Status zu klären, wende eine ausdrücklich festgelegte Risikorichtlinie an und teile dem anfragenden System die Unsicherheit mit. Vermeide automatische, ungeprüfte Wiederholungen.

  • Bewahre die interne ID des Versuchs und, falls vorhanden, die vom Anbieter zurückgegebene Referenz auf.
  • Unterscheide zwischen „keine Antwort erhalten“ und „Anfrage vom Anbieter abgelehnt“.
  • Wenn ein weiterer Versuch zulässig ist, protokolliere ihn als neuen Versuch und verknüpfe ihn mit dem ungewissen Versuch.
  • Versprich nicht, dass der Nutzer nur eine SMS erhält, wenn sich das Ergebnis des ersten Versuchs nicht bestätigen lässt.

Interne Deduplizierung und Anbieter-Idempotenz sind nicht dasselbe

Die interne Deduplizierung verhindert, dass deine Plattform einen Vorgang, den sie als gleichwertig einstuft, mehrfach verarbeitet. Die Idempotenz einer Versand-API kann – sofern dokumentiert und verfügbar – dazu führen, dass der Anbieter wiederholte Anfragen nach den Regeln dieser API erkennt. Das eine schließt das andere nicht ein.

Bevor du dich auf eine externe Garantie verlässt, prüfe die technische Dokumentation der verwendeten API oder Verbindung. Kläre den Gültigkeitsbereich des Schlüssels, seine Speicherdauer, die Antwort auf eine Wiederholung und ob der Mechanismus den konkreten Timeout-Fall tatsächlich abdeckt. Gehe nicht davon aus, dass HTTP oder SMPP allein eine geschäftliche Deduplizierung für OTPs bereitstellen.

  • Behalte eine interne Steuerung bei, auch wenn der Anbieter Idempotenz unterstützt.
  • Sende denselben Schlüssel nicht ungeprüft erneut, ohne seinen dokumentierten Gültigkeitsbereich und sein Verhalten zu kennen.
  • Wenn es keine dokumentierte Garantie gibt, behandle wiederholte Sendungen als potenzielle Duplikate.

Nachvollziehbarkeit und Grenzen der Interpretation sicherstellen

Protokolliere die Operations-ID, die ID jedes Versuchs, Statusübergänge, den Grund für einen erneuten Versand und das vom Anbieter gemeldete Ergebnis. Begrenze die erfassten personenbezogenen Daten und wende Zugriffskontrollen sowie Aufbewahrungsfristen an, die deinen Verpflichtungen entsprechen. Mit diesen Protokollen lässt sich nachvollziehen, welche Entscheidungen die einzelnen Komponenten bei gleichzeitigen Anfragen getroffen haben.

Beschreibe einen vom Anbieter gemeldeten Status auch als solchen. Leite daraus keine Aussage über den Empfang auf dem Telefon ab, wenn es dafür keinen Nachweis gibt. Versandprotokolle dienen dem Betrieb und der Fehlerdiagnose; sie garantieren weder, dass der Nutzer die SMS erhalten hat, noch, dass er die Verifizierung abgeschlossen hat.

  • Unterscheide das Ergebnis der Anfrage an deine API, das vom Anbieter gemeldete Ergebnis und das Ergebnis der OTP-Verifizierung.
  • Vermerke den Grund für einen erneuten Versand und wer oder welche Komponente ihn freigegeben hat.
  • Nutze korrelierbare IDs in internen Protokollen, ohne den OTP-Code in Telemetriedaten offenzulegen.
  • Teste wiederholte und parallele Anfragen, Fehler zwischen Statusübergängen sowie Timeouts mit unbestätigten Ergebnissen.
FAQ

Häufige Fragen

Garantiert ein Operationsschlüssel, dass nur eine SMS versendet wird?

Nein, nicht für sich allein. Er kann helfen, zu verhindern, dass deine Anwendung einen gleichwertigen Vorgang mehrfach verarbeitet, sofern die Beanspruchung atomar koordiniert wird. Er garantiert nicht, dass ein Anbieter nur eine Anfrage erhalten hat oder dass die Nachricht nur einmal auf dem Endgerät ankommt.

Sollte ich jedes Mal ein neues OTP erstellen, wenn der Nutzer auf „Erneut senden“ tippt?

Dafür gibt es keine allgemeingültige Regel. Lege fest, ob die Aktion eine noch gültige Verifizierung wiederholt oder eine neue startet, und bestimme, wie der vorherige Code behandelt wird. Die Richtlinie muss bei Erstellung, Ablauf, Ungültigmachung und Validierung einheitlich sein.

Was ist zu tun, wenn der Versand mit einem Timeout endet?

Behandle das Ergebnis als ungewiss, wenn du nicht feststellen kannst, ob der Anbieter die Anfrage erhalten hat. Nutze die dokumentierten verfügbaren Mechanismen und gehe nicht davon aus, dass die Nachricht nicht angenommen wurde. Ist ein weiterer Versuch zulässig, protokolliere die Entscheidung und verknüpfe sie mit dem vorherigen Versuch.

Bestätigt ein Zustellstatus, dass die SMS auf dem Telefon angekommen ist?

Setze beides nicht ohne konkreten Nachweis gleich. Erfasse den Status als vom Anbieter gemeldetes Ergebnis und unterscheide ihn sowohl vom bestätigten Empfang auf dem Endgerät als auch vom Abschluss der Authentifizierung.

Verwendete Quellen

  1. NIST Digital Identity GuidelinesNIST
  2. OWASP Authentication Cheat SheetOWASP
  3. ClickSend: Envíos de OTP por SMSClickSend