In der Testumgebung war die API durchgehend erreichbar. Sobald das Archiv jedoch an das Testteam übergeben wurde, trat unmittelbar ein Netzwerkfehler auf. Noch gefährlicher ist der umgekehrte Fall: Um ein Zertifikatsproblem vorübergehend zu umgehen, bleibt NSAllowsArbitraryLoads versehentlich in der Release-Konfiguration. Die App scheint wieder zu funktionieren, lässt damit aber sämtliche Verbindungen zu, die ATS eigentlich blockieren sollte. Um beide Fehlerklassen zu verhindern, darf sich die Prüfung nicht auf die Info.plist im Repository beschränken. Maßgeblich ist die App, die tatsächlich auf dem Cloud-Mac gebaut wurde.
Zuerst den Umfang des Prüftors festlegen
Ein belastbares ATS-Prüftor umfasst mindestens drei Ebenen: die statische Konfiguration, den TLS-Handshake auf Hostebene und die Anfrage auf Anwendungsebene. Jede Ebene beantwortet andere Fragen. Ein einzelner erfolgreicher curl-Aufruf kann daher nicht die gesamte Prüfung ersetzen.
| Ebene | Prüfobjekt | Bei Fehlern zuerst prüfen |
|---|---|---|
| Statische Konfiguration | Finale Info.plist in der App | Build-Konfiguration, Änderungen durch Skripte, Domainausnahmen |
| TLS-Test | Verbindung vom Cloud-Mac zum Zielendpunkt | DNS, Zertifikatskette, Protokollversion, Weiterleitungen |
| App-Anfrage | Tatsächliches Verhalten von URLSession | ATS, Sitzungskonfiguration, Authentifizierung und Antwortverarbeitung |
Zunächst muss die Richtlinie eindeutig sein: Release-Artefakte dürfen NSAllowsArbitraryLoads nicht enthalten. Unter NSExceptionDomains dürfen nur geprüfte Domains aufgeführt sein. Temporäre Ausnahmen benötigen eine verantwortliche Person und klar definierte Bedingungen für ihre Entfernung. Einstellungen für das lokale Debugging dürfen nicht in Release einfließen.
Eine ATS-Ausnahme ist kein universeller Schalter, um „das Netzwerk erst einmal zum Laufen zu bringen“, sondern eine sicherheitsrelevante Änderung, deren Umfang, Grund und Rückbauplan dokumentiert werden müssen.
Das fertige Build-Artefakt prüfen
Die plist im Quellcode kann durch INFOPLIST_KEY_*, unterschiedliche xcconfig-Dateien oder Build-Skripte überschrieben werden. Deshalb wird zuerst ein Release-Build erstellt und anschließend der Pfad des Artefakts ermittelt:
set -euo pipefail
xcodebuild \
-scheme "$SCHEME" \
-configuration Release \
-sdk iphonesimulator \
-derivedDataPath "$PWD/.derived-data" \
build
APP_PATH="$(find "$PWD/.derived-data/Build/Products" \
-type d -name '*.app' -path '*Release-*' -print -quit)"
test -n "$APP_PATH"
PLIST="$APP_PATH/Info.plist"
plutil -lint "$PLIST"
plutil -extract NSAppTransportSecurity json -o - "$PLIST" \
> "$PWD/ats-effective.json" 2>/dev/null || printf '{}
' > "$PWD/ats-effective.json"
Das Prüftor sollte sowohl ein fehlendes ATS-Dictionary als auch ein vorhandenes, aber leeres Dictionary als regulären Zustand behandeln. Fehlschlagen muss die Prüfung erst bei pauschalen Freigaben oder nicht genehmigten Ausnahmen. Das folgende Skript übernimmt die Positivliste aus einer Umgebungsvariable, damit interne Domains des Teams nicht fest in ein öffentliches Skript geschrieben werden:
import json
import os
import sys
with open(sys.argv[1], encoding="utf-8") as f:
ats = json.load(f)
if ats.get("NSAllowsArbitraryLoads") is True:
raise SystemExit("NSAllowsArbitraryLoads is forbidden")
approved = {
item.strip().lower()
for item in os.getenv("ATS_APPROVED_DOMAINS", "").split(",")
if item.strip()
}
exceptions = ats.get("NSExceptionDomains", {})
unknown = sorted(set(map(str.lower, exceptions)) - approved)
if unknown:
raise SystemExit("Unapproved ATS domains: " + ", ".join(unknown))
Ausgeführt wird es mit python3 ci/audit_ats.py ats-effective.json. Die CI-Protokolle dürfen Schlüsselnamen und Prüfergebnisse enthalten, nicht jedoch Anfrage-Token, Cookies oder vollständige Authentifizierungs-Header.
Eine prüfbare Liste der Ausnahmen führen
Ein reiner Vergleich der Domains reicht nicht aus. Für jede Ausnahme müssen die erlaubten ATS-Schlüssel, die jeweilige Umgebung, der Grund und die Bedingungen für eine erneute Prüfung dokumentiert werden. Besondere Aufmerksamkeit erfordert NSIncludesSubdomains: Dieser Schlüssel erweitert den Geltungsbereich auf sämtliche Subdomains und darf nicht standardmäßig aktiviert werden, nur weil derzeit lediglich eine API verwendet wird.
Die Liste sollte als JSON- oder YAML-Datei im Repository gepflegt werden. Im Review sind mindestens die folgenden Punkte zu prüfen:
- Domains müssen exakt angegeben werden; Beschreibungen mit Platzhaltercharakter sind nicht zulässig.
- Die TLS-Version darf nicht unter die im Projekt festgelegte Mindestanforderung abgesenkt werden.
- Wegen einer einzelnen Weiterleitung darf nicht die gesamte übergeordnete Domain freigegeben werden.
- Debug-Endpunkte dürfen ausschließlich in die Debug-Konfiguration aufgenommen werden.
- Nach dem Entfernen einer Ausnahme müssen Build und Anfragetests erneut ausgeführt werden.
Konfigurationslecks zwischen Build-Varianten erkennen
Debug und Release werden getrennt gebaut. Anschließend werden zwei Fassungen von ats-effective.json exportiert und miteinander verglichen. Enthält Release Schlüssel, die ausschließlich für das Mitschneiden von Netzwerkverkehr oder für lokale Dienste vorgesehen sind, muss die Prüfung unmittelbar fehlschlagen. Ein Vergleich der Quelldateien allein genügt nicht, da selbst dieselbe plist durch unterschiedliche Build-Einstellungen zu verschiedenen Ergebnissen führen kann.
TLS und Weiterleitungsketten prüfen
Nach erfolgreicher statischer Prüfung wird die Zieladresse direkt von dem Cloud-Mac getestet, der auch den Build ausführt. nscurl gibt eine ATS-Diagnosematrix aus und eignet sich damit zur Untersuchung von Protokollversionen, Zertifikatsketten und Forward Secrecy:
test -n "${API_URL:-}"
/usr/bin/nscurl --ats-diagnostics "$API_URL" \
> "$PWD/ats-diagnostics.txt" 2>&1
Diese Ausgabe ist für die Diagnose geeignet, sollte aber nicht allein deshalb als bestanden gelten, weil die Datei den Text „PASS“ enthält. Der Diagnosemodus probiert mehrere Kombinationen mit gelockerten Anforderungen aus. Für die verbindliche CI-Entscheidung muss eine kontrollierte Anfrage an die reale Projekt-URL gesendet werden. Dabei sind Zeitlimit, Anzahl der Weiterleitungen und Antwortcode einzuschränken:
curl --fail --silent --show-error \
--proto '=https' \
--tlsv1.2 \
--max-time 15 \
--max-redirs 3 \
--output /dev/null \
"$API_URL"
Ein erfolgreicher curl-Aufruf bestätigt lediglich, dass die Verbindung auf Hostebene funktioniert. Er wendet weder die ATS-Konfiguration der iOS-App an noch belegt er, dass Sitzungs-Proxy, Anfrage-Header oder Authentifizierungslogik der App korrekt arbeiten.
Regressionstest auf Anwendungsebene ergänzen
Abschließend wird ein schlankes Test-Target hinzugefügt, das den produktiven Netzwerk-Stack verwendet. Der Test liest eine von der Testumgebung bereitgestellte URL ein, führt über URLSession einen Health Check aus und prüft, ob die Anfrage abgeschlossen wurde, der Statuscode der Vereinbarung entspricht und keine Weiterleitung zu einer Nicht-HTTPS-Adresse erfolgt ist. Feste Zugangsdaten dürfen nicht im Testcode hinterlegt werden.
Ausreichende, aber nicht übermäßige Nachweise sichern
Bei einem Fehler genügt es, die folgenden Daten zu archivieren: das finale ATS-Dictionary, das Ergebnis des Abgleichs der Domainlisten, die Ausgabe von nscurl, den HTTP-Statuscode, den Hostnamen des Weiterleitungsziels sowie den Namen der Xcode-Build-Konfiguration. Der vollständige Inhalt von Zertifikaten, Zugriffstoken und komplette Antworttexte gehören in der Regel nicht in dauerhaft aufbewahrte Protokolle.
Die Reihenfolge sollte fest vorgegeben sein: „Release-Build → Prüfung der finalen plist → TLS-Test → URLSession-Test“. So werden sowohl Konfigurationsabweichungen erkannt als auch Fehlerquellen voneinander getrennt: Verweigert eine App-Richtlinie die Verbindung, oder haben sich Zertifikatskette, DNS beziehungsweise Weiterleitungen des Zielendpunkts geändert?
Checkliste vor dem Release
Vor dem Zusammenführen ist zu bestätigen, dass das Release-Artefakt keine pauschale Netzwerkfreigabe enthält, alle Domainausnahmen in der genehmigten Liste stehen, der Geltungsbereich für Subdomains ausdrücklich geprüft wurde, der TLS-Test die reale Zieladresse verwendet und der Test auf Anwendungsebene mit der tatsächlichen URLSession-Konfiguration arbeitet. Bei einem Fehler sollten zunächst das Artefakt und die Diagnosedateien gesichert werden, bevor die Konfiguration geändert wird. Neue Ausnahmen dürfen keine Zertifikats- oder Weiterleitungsprobleme verdecken.
Dieses Prüftor soll nicht alle Netzwerkfehler automatisch beseitigen. Es ordnet sie einer konkret bearbeitbaren Ebene zu und stellt sicher, dass temporäre Debug-Einstellungen nicht unbemerkt in das nächste Release gelangen.
Häufig gestellte Fragen
Warum reicht eine Prüfung der Info.plist im Quellcode nicht aus?
Build-Einstellungen, Konfigurationsdateien und Skripte können die Werte verändern. Maßgeblich ist deshalb die Info.plist im fertigen App-Bundle.
Garantiert ein erfolgreicher nscurl-Test die Verbindung der App?
Nein. Zusätzlich ist ein URLSession-Test erforderlich, der ATS-Regeln, Weiterleitungen und den vorgesehenen Authentifizierungsweg abdeckt.
Welche ATS-Einstellungen müssen die Pipeline stoppen?
NSAllowsArbitraryLoads, nicht genehmigte Domänenausnahmen und versehentlich ausgelieferte Debug-Freigaben sollten den Build unmittelbar stoppen.
Die nächste Entwicklungs- oder Build-Aufgabe auf einem Cloud-Mac mini ausführen
Wählen Sie zwischen zwei M4-Konfigurationen, vier Mietlaufzeiten und fünf verfügbaren Standorten. Maßgeblich ist der vom Control Panel in Echtzeit angezeigte Status.