Backup-Download kann nach Unterbrechung nicht fortgesetzt werden, da der E-Mail-Token bereits verwendet wurde

Es gab ein vorheriges Problem mit dem Backup-Download, das offenbar in 2026.01.0-latest behoben wurde. Ich glaube, dass es sich hier um ein separates, aktuelles Problem handelt, das die Wiederherstellung nach einem unterbrochenen Backup-Download betrifft, und nicht um den alten Fehler beim initialen Download.

Eine Möglichkeit, die in der früheren Diskussion angesprochen wurde, war, dass der einmalige Backup-Link einen Browser daran hindern könnte, einen unterbrochenen Download fortzusetzen: Sobald der erste Request den Link verbraucht hat, ist ein nachfolgender Resume-Request möglicherweise nicht mehr autorisiert.

Ich konnte dieses Verhalten nun auf der aktuellen Discourse-Version genau reproduzieren.

Reproduktion

Ich habe ein lokales Discourse-Backup heruntergeladen mit:

4.231.639.143 Bytes

verwendet auf iOS mit Safari.

Der Download verlief zunächst normal. Ich habe eine Bildschirmaufnahme, die zeigt, wie er von ungefähr:

https://youtube.com/shorts/_Pjc-kxJ1OE?feature=shared

313,3 MB auf 317,8 MB anwuchs.

Ich habe dann Safari für nur etwa 15 Sekunden verlassen.

Als ich zu Safari zurückkehrte, hatte der Download bei ungefähr:

319,8 MB

gestoppt, und Safari zeigte seine Wiederholungssteuerung an.


Das Caddy-Zugriffsjournal für den ursprünglichen Request zeigt:

GET /admin/backups/...

HTTP/3

Range: none

status: 200

Content-Length: 4231639143

size: 319864024

Ich habe dann Safaris eigene Wiederholungssteuerung verwendet.

Safari stellte einen echten HTTP-Byte-Range-Request auf:

GET /admin/backups/...

HTTP/3

Range: bytes=319799904-

status: 422

Safari versuchte also nicht einfach, einen vollständigen neuen Download zu starten. Es versuchte korrekt, die bestehende Datei ungefähr an dem Punkt fortzusetzen, an dem die vorherige Übertragung stoppte.

Discourse lehnte diesen Range-Request mit 422 ab.

Aktueller Discourse-Code

Wenn man sich den aktuellen Admin::BackupsController#show ansieht, ist die Abfolge im Wesentlichen:

EmailBackupToken.compare(current_user.id, token)

        ↓

find backup

        ↓

EmailBackupToken.del(current_user.id)

        ↓

send_file

Der initial autorisierte Request verbraucht also das E-Mail-Token, bevor die Übertragung der mehreren Gigabyte tatsächlich abgeschlossen ist.

Wenn diese Übertragung anschließend unterbrochen wird, stellt Safari einen weiteren Request wie:

Range: bytes=319799904-

aber dieser Request geht erneut durch /admin/backups/… und das E-Mail-Token wurde bereits gelöscht.

Das scheint den 422-Fehler zu erklären, den ich erfasst habe.

Die autorisierte Sitzung wird ebenso wie das Backup-Token benötigt, daher schlage ich nicht vor, das bestehende Token einfach unbegrenzt wiederverwendbar zu machen.

Ich frage mich vielmehr, ob es einen begrenzten Weg geben sollte, damit derselbe autorisierte Administrator denselben bereits autorisierten Backup-Download fortsetzen kann.

Zum Beispiel könnte dies eine kurzlebige Download-Autorisierung sein, die mit demselben Benutzer und demselben Backup verknüpft ist, obwohl die Entwickler vielleicht eine bessere Lösung für dieses Problem haben.

Warum sich dies auf iOS leichter reproduzieren lässt

Die Unterbrechung, die dies aufdeckte, scheint auch Berichten von Nutzern neuerer iOS-Versionen zu ähneln.

Es gibt mindestens drei Berichte, die auf der Apple Community gehostet werden:

ältere Berichte

„Safari-Downloads im Hintergrund schlagen nach iOS-26-Update fehl“ — Oktober 2025

Safari Background Downloads Failing After… - Apple Community

Der Nutzer berichtet, dass größere Safari-Downloads fehlschlagen, wenn sie im Hintergrund ausgeführt werden, nachdem auf iOS 26 aktualisiert wurde. Sie haben auch ein iPhone 15 Pro auf iOS 26 aktualisiert und dasselbe Verhalten gemeldet.

„Safari-iOS-Hintergrund-Download-Problem – Stornierung bei Minimierung“ — Dezember 2025

Safari iOS Background Download Issue - Ca… - Apple Community

Gemeldet auf einem iPhone 15 mit iOS 26.1. Downloads starten normal, werden aber storniert, wenn zu einer anderen App gewechselt oder Safari minimiert wird.

„Safari kann einen großen Download nicht abschließen“ — April 2026

Gemeldet auf einem iPhone 17 Pro Max mit iOS 26.4. Große Downloads pausieren angeblich kurz nach dem Sperren des Geräts, dem Verlassen von Safari oder dem Wechsel zwischen Mobilfunk und WLAN. Der Autor berichtet speziell von gutem Empfang und sagt, dass dieselben Downloads auf PC, Mac und Android abgeschlossen werden.

Ich denke, diese Community-Berichte reichen nicht aus, um zu sagen, dass Apple einen iOS-Regression bestätigt hat.

Allerdings sind sie mit meiner Bildschirmaufnahme konsistent: Das Backup wurde normal heruntergeladen, ich habe Safari kurz verlassen, und als ich zurückkehrte, war die Übertragung unterbrochen.

Der wichtige Punkt auf der Discourse-Seite ist unabhängig davon, warum die Unterbrechung auftrat:

große Backup-Übertragung wird unterbrochen

        ↓

Browser versucht standardsbasiertes Range-Resume

        ↓

Backup-Token wurde bereits verbraucht

        ↓

Resume erhält 422

Beobachtungen auf Server-Ebene

Dies scheint kein allgemeines Problem mit dem aktuellen nginx/sendfile-Pfad zu sein.

Der gleiche Backup-Pfad mit mehreren GB wurde erfolgreich abgeschlossen:

  • von Firefox/Linux über HTTP/3; und
  • vom selben iPhone über WLAN unter Verwendung von HTTP/3.

Das aktuelle nginx bewirbt auch die Unterstützung für Byte-Ranges.

Ich schlage also nicht vor, dass HTTP/3, Caddy oder nginx an sich nicht in der Lage wäre, das Backup zu liefern.

In dieser bestimmten Reproduktion wurde die ursprüngliche Übertragung nach etwa 320 MB unterbrochen, Safari stellte anschließend einen gültig wirkenden Range-Request, und dieser zweite Request wurde von Discourse abgelehnt.

Würte es Sinn ergeben, dass ein bereits autorisierter Backup-Download einen kurzlebigen Mechanismus hat, der es demselben autorisierten Administrator ermöglicht, diese gleiche Datei nach einer unterbrochenen Übertragung fortzusetzen?

Ich habe mir den Verlauf und die aktuelle Testabdeckung angesehen, um mir ein besseres Bild davon zu machen, was eine mögliche Lösung erhalten müsste.

Das Einmal-Verhalten geht auf die Sicherheitsänderung von März 2017 zurück:

Dieser Commit führte per E-Mail gesendete Backup-Download-Tokens ein und verbrauchte das Token sofort, sobald die Download-Anfrage autorisiert worden war.

Die gleiche Reihenfolge besteht in der aktuellen Admin::BackupsController#show-Methode weiterhin:

Die zugrunde liegende Token-Implementierung ist ebenfalls noch sehr einfach:

EmailBackupToken speichert ein Redis-Token pro Benutzer mit einer Ablaufzeit von einem Tag. Es ist benutzerspezifisch und nicht an ein bestimmtes Backup gebunden.

Bei der Durchsicht der Historie dieser Datei scheinen die Änderungen seit 2017 mechanischer bzw. stilistischer Natur zu sein und keine Änderungen am Autorisierungsmodell darzustellen.

Daher scheint es unerwünscht zu sein, das bestehende per E-Mail gesendete Token einfach wiederverwendbar zu belassen: Das würde die ursprüngliche Einmal-Beschränkung abschwächen, und das Token selbst ist nicht auf das spezifische Backup in der URL beschränkt.

Die aktuellen Request-Tests finden sich hier:

Sie decken einen initialen gültigen Download, ungültige Tokens, ungültige/fehlende Backup-IDs und Berechtigungen ab, aber ich konnte keine Abdeckung für eine unterbrochene Übertragung, gefolgt von einer Range:-Anfrage, finden.

Die API-Request-Spezifikation dokumentiert ebenfalls den üblichen Download-Pfad mit Dateiname + Token:

Eine mögliche Richtung für einen Patch wäre daher, das bestehende Einmal-E-Mail-Token für die Einleitung des Backup-Downloads beizubehalten, während eine separate, kurzlebige Resume-Autorisierung für das bereits autorisierte lokale Backup bereitgestellt wird.

Ich würde erwarten, dass eine solche Resume-Autorisierung eng auf den authentifizierten Benutzer und das spezifische Backup beschränkt ist und eine nachfolgende Byte-Bereichs-Anfrage ermöglicht, ohne das ursprüngliche ein-tägige, benutzerweite E-Mail-Token allgemein wiederverwendbar zu machen.

Die Verhaltensweisen, die ich beibehalten bzw. hinzufügen würde, sind:

  • Die initiale GET-Anfrage mit einem gültigen per E-Mail gesendeten Token bleibt zulässig;
  • Eine weitere übliche vollständige GET-Anfrage mit dem verbrauchten E-Mail-Token bleibt abgelehnt;
  • Ein Byte-Bereichs-Neustart für dasselbe Backup durch denselben authentifizierten Admin kann für einen kurzen, begrenzten Zeitraum fortgesetzt werden;
  • Ein Resume-Versuch für ein anderes Backup bleibt abgelehnt;
  • Eine abgelaufene Resume-Autorisierung bleibt abgelehnt.

Ich habe nicht davon ausgegangen, dass dies unbedingt die Implementierung ist, die die Maintainer bevorzugen würden, aber die Code- und Testfläche scheint ziemlich überschaubar zu sein.

Ich bin bereit, einen Entwurf für einen Patch und Regressionstests für die Safari-Range:-Reproduktion zu erstellen, wenn dieses allgemeine Sicherheitsmodell sinnvoll erscheint.