ZIP-Import in iOS: Pfade und Entpacklimits auf dem Mac testen

ZIP-Import in iOS: Pfade und Entpacklimits auf dem Mac testen

ZIP-Import in iOS: Pfade und Entpacklimits auf dem Mac testen

Ein Nutzer importiert eine ZIP-Datei. Die App meldet Erfolg, hat aber Dateien außerhalb des Importverzeichnisses abgelegt. Meist liegt das nicht am Kompressionsverfahren, sondern daran, dass Eintragsnamen ungeprüft mit dem Ausgabepfad verknüpft werden. Wenn Sie diesen Ablauf auf einem Cloud-Mac testen, prüfen Sie zuerst die Pfadregeln und binden Sie erst danach den Entpacker des Projekts ein. Verwenden Sie keine echten Nutzerdaten für manipulierte Testarchive und lassen Sie Tests niemals in das Quellcodeverzeichnis schreiben.

Die Grenzen des Imports festlegen

Für diesen Import sind ausschließlich reguläre Dateien und Verzeichnisse zulässig. Symbolische Links und unbekannte Eintragstypen werden abgelehnt. Jeder Import erhält ein eigenes Staging-Verzeichnis. Erst nach vollständiger Prüfung werden die Daten in das Anwendungsverzeichnis verschoben. Schlägt auch nur ein Eintrag fehl, wird der gesamte Staging-Bestand gelöscht; bereits erfolgreich verarbeitete Teile bleiben nicht erhalten.

Legen Sie drei projektbezogene Obergrenzen fest: höchstens tausend Einträge, höchstens zwanzig Megabyte pro Datei und höchstens hundert Megabyte entpackte Daten insgesamt. Das sind beispielhafte Richtlinien, keine Systemgrenzen; das Team sollte sie an die Anforderungen der Anwendung anpassen. Die Vorprüfung verwendet die Größenangaben im ZIP-Verzeichnis, beim tatsächlichen Schreiben werden zusätzlich die realen Bytezahlen aufsummiert. Keine der beiden Prüfungen ersetzt die andere.

Die Sandbox ist keine Sicherheitsgrenze für den Entpacker. Selbst wenn ein manipulierter Eintrag den App-Container nicht verlassen kann, kann er darin noch immer Datenbanken oder Konfigurationsdateien überschreiben.

Eine ausführbare Pfadvorprüfung schreiben

Speichern Sie den folgenden Code als zip-path-check.swift und führen Sie auf dem Cloud-Mac xcrun swift zip-path-check.swift aus. Das Skript prüft ausschließlich Eintragsnamen; es liest keine ZIP-Dateien. Im Projekt müssen die vom Entpackungsframework gelieferten Namen und Eintragstypen derselben Richtlinie unterzogen werden. Ein erfolgreicher Skriptlauf bedeutet nicht, dass der gesamte Importablauf sicher ist.

import Foundation

enum PathError: Error {
    case invalid
}

func safeComponents(_ name: String) throws -> [String] {
    guard !name.isEmpty,
          !name.hasPrefix("/"),
          !name.contains("\\"),
          !name.contains(":"),
          !name.unicodeScalars.contains(where: {
              CharacterSet.controlCharacters.contains($0)
          }) else {
        throw PathError.invalid
    }

    let body = name.hasSuffix("/") ? String(name.dropLast()) : name
    let parts = body.split(
        separator: "/",
        omittingEmptySubsequences: false
    ).map(String.init)

    guard !parts.isEmpty,
          parts.allSatisfy({
              !$0.isEmpty && $0 != "." && $0 != ".."
          }) else {
        throw PathError.invalid
    }
    return parts
}

let cases: [(String, Bool)] = [
    ("docs/readme.txt", true),
    ("docs/", true),
    ("report..txt", true),
    ("../outside.txt", false),
    ("docs/../../outside.txt", false),
    ("/tmp/outside.txt", false),
    ("docs//readme.txt", false),
    ("docs/./readme.txt", false),
    ("docs\\..\\outside.txt", false),
    ("C:/outside.txt", false),
    ("docs/\u{0000}bad.txt", false)
]

for (name, expected) in cases {
    let accepted = (try? safeComponents(name)) != nil
    precondition(accepted == expected, "Path policy mismatch")
}
print("Path checks passed")

Diese Importregeln sind bewusst restriktiv: Erlaubt sind nur relative Pfade mit Schrägstrichen als Trennzeichen. Bei Verzeichnissen ist ein einzelner abschließender Schrägstrich zulässig. Auch Doppelpunkte und Steuerzeichen werden abgelehnt, um unterschiedliche Interpretationen durch verschiedene Dateisysteme zu vermeiden. Falls die Anwendung solche Zeichen tatsächlich benötigt, ergänzen Sie gezielte Testfälle, statt sämtliche Prüfungen pauschal zu lockern.

Links und doppelte Ziele prüfen

Auch wenn ein Pfad die Prüfung besteht, muss der Eintragstyp noch geprüft werden. Entpacken Sie keinen Link, um ihm anschließend beim Schreiben des nächsten Eintrags zu folgen. Leiten Sie den Typ auch nicht aus der Dateierweiterung ab. Verwenden Sie die Typinformationen und Attribute, die das Entpackungsframework bereitstellt. Typen, die sich nicht zuverlässig bestimmen lassen, werden grundsätzlich abgelehnt.

Erstellen Sie einen Index aller Zielpfade anhand ihrer Komponenten. Neben identischen Zielen müssen auch Konflikte zwischen übergeordneten Pfaden und Unterpfaden erkannt werden, etwa wenn zuerst die Datei docs und danach docs/a.txt geschrieben werden soll. Auch die umgekehrte Reihenfolge muss fehlschlagen. Andernfalls hängt das Ergebnis von der Sortierung der Archiveinträge ab.

Für Namen, die sich nur in der Groß- und Kleinschreibung unterscheiden oder unter Unicode äquivalent sind, ist eine zusätzliche Regel erforderlich. Muss das importierte Material diese Namen nicht unterscheiden, können Sie die Pfadkomponenten für die Konflikterkennung kanonisch zusammengesetzt normalisieren und per Case-Folding vereinheitlichen. Die tatsächlichen Namen bleiben dabei unverändert. Schließen Sie nicht vom Verhalten des aktuellen Volumes auf dem Cloud-Mac auf das Schreibverhalten unter iOS. Bereiten Sie mindestens zwei Gruppen von Testfällen vor — Kollisionen durch Groß- und Kleinschreibung sowie durch zusammengesetzte Unicode-Zeichen — und prüfen Sie sie erneut im Zielsimulator.

Größenlimits beim tatsächlichen Schreiben prüfen

Die Adapterschicht muss die entpackten Datenblöcke erhalten, bevor sie geschrieben werden. Sie prüft zunächst die bisherige Gesamtgröße und schreibt erst danach in die Datei. Vergleichen Sie dabei, ob das verbleibende Kontingent ausreicht, statt Größen direkt zu addieren und dadurch einen Integer-Überlauf zu riskieren. Kann die Bibliothek Daten nur vollständig in den Arbeitsspeicher entpacken und den Vorgang nicht währenddessen begrenzen, müssen Sie eine andere Schnittstelle verwenden oder die Eingabedaten anderweitig streng beschränken.

Legen Sie für jedes Testarchiv das erwartete Ergebnis fest: Ein normales Verzeichnis wird akzeptiert, ein Pfad außerhalb des zulässigen Bereichs wird abgelehnt, doppelte Ziele werden abgelehnt und jede Überschreitung eines Limits wird abgelehnt. Ergänzen Sie beschädigte komprimierte Daten und prüfen Sie, dass Lesefehler nicht als reguläres Ende behandelt werden. Auch bei gültigen Archiven müssen Sie die von der Bibliothek gemeldeten Ergebnisse der Integritätsprüfung auswerten. Es reicht nicht, nur die Existenz der fertigen Dateien zu prüfen.

Prüfen Sie nach einem Schreibfehler per Assertion, dass das Staging-Verzeichnis gelöscht wurde, das Anwendungsverzeichnis unverändert ist und der Inhalt einer zuvor neben dem Importverzeichnis abgelegten Wächterdatei ebenfalls unverändert geblieben ist. Das ist aussagekräftiger als die bloße Prüfung, ob eine Funktion einen Fehler auslöst: Der Fehler könnte erst auftreten, nachdem bereits eine Datei außerhalb des zulässigen Bereichs geschrieben wurde.

Regressionstests als Freigabekriterium einbinden und Grenzen absichern

Führen Sie in der Pipeline zuerst das Pfadskript und anschließend die Anwendungstests mit dem tatsächlichen Entpacker aus. Protokollieren Sie die Kennung des Testfalls, den Ablehnungsgrund und die tatsächlich geschriebenen Bytezahlen, aber keine Inhalte von Nutzerdateien. Erstellen Sie zufällig benannte Testverzeichnisse und räumen Sie sie nach Abschluss auf. Das temporäre Systemverzeichnis selbst darf niemals als Entpackungswurzel verwendet werden.

Zwischen Vorprüfung und Schreiben kann weiterhin ein Verzeichnis ausgetauscht werden. Verwenden Sie deshalb ein app-eigenes Staging-Verzeichnis, das andere Aufgaben nicht verändern können, und verhindern Sie beim Schreiben das Folgen von Links. Bei gemeinsam genutzten Desktop-Skripten, deren Verzeichnisse andere Prozesse ändern dürfen, beseitigt eine Prüfung des Zeichenkettenpräfixes dieses Risiko nicht.

Vergewissern Sie sich abschließend, dass drei Bedingungen erfüllt sind: Ein fehlgeschlagener Import hinterlässt keine Teildaten, ein erfolgreicher Import erzeugt ausschließlich die erwarteten Dateien, und die Limits werden anhand der tatsächlichen Bytezahlen durchgesetzt. Die Pfadfunktion ist die erste Prüfschranke. Die abschließende Abnahme erfolgt erst durch Assertions zu den Schreibvorgängen mit echten Archiven.

Häufig gestellte Fragen

Reicht es, Dateinamen mit zwei Punkten abzulehnen?

Nein. Lehnen Sie die eigenständige Pfadkomponente .., absolute Pfade und nicht unterstützte Trennzeichen ab. Prüfen Sie außerdem Links, doppelte Ziele und Verzeichnisgrenzen beim Schreiben. report..txt ist allein kein Angriff.

Verhindern die Größenangaben im ZIP-Verzeichnis Entpackbomben?

Nicht allein. Nutzen Sie sie zur Vorprüfung und zählen Sie beim Schreiben die tatsächlich entpackten Bytes. Begrenzen Sie Einzelgröße, Gesamtgröße und Anzahl der Einträge. Bei Überschreitung abbrechen und das temporäre Verzeichnis löschen.

Build-Warteschlange an einen exklusiven Cloud-Mac übergeben

Wählen Sie einen physischen Knoten, ein Modell und einen Abrechnungszeitraum. So laufen Xcode-Builds und Automatisierungsaufgaben in Ihrer eigenen macOS-Umgebung. Der aktuelle Verfügbarkeitsstatus wird in Echtzeit in der Konsole angezeigt.

Build-Umgebung weiter prüfen

Vergleichen Sie die Modellspezifikationen mit der Anleitung, um die nächsten Einstellungen festzulegen.