c't 9/2021
S. 152
Praxis
Certificate Pinning für iOS-Apps
Bild: Thomas Kuhlenbeck

Unter der Lupe

Apps für die Certificate-Pinning-Funktion von iOS 14 konfigurieren

Will beispielsweise eine Banking-App sicherstellen, dass sie tatsächlich mit dem Server der Bank spricht, dann sollte sie mehr tun, als die Gültigkeit des TLS-Zertfikats vom Betriebs­system prüfen zu lassen. In solchen Fällen kommt häufig Certificate Pinning zum Einsatz. Mit iOS 14 können Entwickler diesen Extraschutz einbauen, ohne groß den Code anzufassen – und da­von profitieren auch die Nutzer.

Von Andreas Kurtz

Ein TLS-Zertifikat, welches von einem vertrauenswürdigen Herausgeber signiert wurde, belegt nicht zwangsläufig, dass der kontaktierte Server auch derjenige ist, für den er sich ausgibt. Auch Angreifer können solche Zertifikate besitzen, etwa wenn es ihnen gelingt, ein Schlupfloch bei einer Zertifizierungsstelle (Certificate Authority, CA) zu finden, die von den Betriebssystemen als vertrauenswürdig eingestuft wird. So was ist bereits passiert. Auch Trojaner und Analyse-Tools klinken sich mit Zertifikaten in verschlüsselte Verbindungen ein. Will eine App ­sicherstellen, dass sie tatsächlich mit dem richtigen Server spricht, dann muss sie genauer hinschauen: Sie kann zum Beispiel überprüfen, ob ein bestimmtes Zertifikat zum Einsatz kommt oder es von einer bestimmten CA signiert wurde. Präsentiert ihr der Server dann ein anderes Zertifikat, kommt die Verbindung nicht zustande. Dieses Verfahren bezeichnet man als Certificate Pinning, weil bestimmte Zertifikate fest an die App gepinnt werden.

Nicht alle Apps nutzen Pinning, besonders bei kritischen Anwendungsfällen wie Online-Banking darf der Nutzer inzwischen erwarten, dass Pinning zum Einsatz kommt. Aber auch allen anderen Apps steht das Plus an Sicherheit gut zu Gesicht. Bislang mussten iOS-Entwickler das Pinning komplett selbst umsetzen – mit iOS 14 geht das nun deutlich einfacher.

Was für Pinning spricht

Beim Aufbau einer verschlüsselten Verbindung über das Transport-Layer-Security-Protokoll (TLS) authentifiziert sich der Server gegenüber der App mit einem Zertifikat. Um sich auf ein gemeinsames Geheimnis für die Verschlüsselung zu verständigen, benutzt die App den öffentlichen Schlüssel aus dem Zertifikat des Servers. Damit die App weiß, dass sie auch wirklich mit dem gewünschten Backend spricht – und nicht etwa mit einem Man-in-the-Middle –, muss sie erst die Ver­trauens­würdigkeit des Zertifikats prüfen. Dazu ist jedes Zertifikat von einer CA signiert.

Das Betriebssystem prüft bei jedem Verbindungsaufbau, ob der in dem Zertifikat angegebene Hostname dem des angefragten Systems entspricht. Zusätzlich muss das präsentierte Zertifikat von einer vertrauenswürdigen Zertifizierungsstelle (Trusted CA) signiert sein. Für diese Überprüfung nutzt iOS einen systemweiten Zertifikatsspeicher (Trust Store), in dem die Root-Zertifikate zahlreicher öffentlicher CAs vorinstalliert sind.

Schaut man sich die Liste der in iOS 14 vorinstallierten Root-Zertifikate aber genauer an, kann einem angst und bange werden: Der Trust Store enthält mehr als 150 als vertrauenswürdig eingestufte Root-Zertifikate von mehr oder weniger bekannten, auch regierungsnahen Zertifizierungsstellen aus aller Herren Länder.

Aufgrund der zahlreichen Vorfälle in der Vergangenheit, vom Ausstellen falscher Zertifikate wie 2011 bei DigiNator bis hin zur Kompromittierung ganzer CAs, ist ein gewisses Misstrauen durchaus angebracht (siehe ct.de/yssq). Um Missbrauch zu verhindern, entstanden in den vergangenen Jahren Kontrollinstrumente wie Certificate Transparency (CT). Trotzdem stellt sich die Frage, ob man tatsächlich allen CAs im Trust Store uneingeschränkt vertrauen möchte.

Außerdem besteht das Risiko, dass Benutzer per Social Engineering sogenannte Konfigurationsprofile oder Mo­bile-Device-Management-Profile unterge­jubelt bekommen und sich so unbemerkt zusätzliche CAs einfangen. Apple erhöhte zwar die Hürden für solche Angriffe, ein gewisses Risiko aber bleibt bestehen.

App-eigene Validierung

Aus diesen Gründen gehen immer mehr Apps dazu über, sich nicht auf die Validierung des Betriebssystems zu verlassen, sondern ihre Zertifikate selbst unter die Lupe zu nehmen. Bei diesem Certificate Pinning wird das erwartete TLS-Zertifikat meist schon während der Entwicklung in der App hinterlegt. Später prüft die Anwendung bei jedem Verbindungsaufbau, ob das Backend auch das erwartete Zertifikat präsentiert.

In der Praxis haben sich verschiedene Pinning-Verfahren etabliert, bei denen entweder das gesamte Zertifikat in der App hinterlegt wird oder nur der im Zer­tifikat enthaltene öffentliche Schlüssel (Public Key Pinning). Bislang mussten App-Entwickler dafür eigenen Code schreiben oder fertige Open-Source-Lösungen wie TrustKit in ihre App integrieren. Mit iOS 14 geht das nun, ohne den Code der App anpassen zu müssen.

Transport Security für Apps

Auf Apples Plattformen gewährleistet schon seit Längerem ein Mechanismus namens App Transport Security (ATS), dass alle Apps HTTPS nutzen und Mindest­anforderungen an eine sichere TLS-Verbindung einhalten. iOS 14 erweitert diesen Mechanismus und bietet Apps die Möglichkeit, die erwarteten öffentlichen Schlüssel ihrer Kommunikationspartner einfach in der zentralen Konfigurationsdatei der App (Info.plist) zu hinterlegen.

Für alle Verbindungen, die eine App dann über das Standard-API initiiert, konkret über das URL Loading System, gleicht iOS die Fingerprints der vom Server ausgelieferten Zertifikate mit den in der Konfiguration hinterlegten Fingerprints der Schlüssel ab. Stimmen die Schlüssel nicht überein, bricht der Verbindungsaufbau ab und iOS antwortet mit einer Fehlermeldung. Auf diese Weise können Angriffe auf die Kommunikation abgewehrt werden, selbst wenn ein Angreifer im Besitz eines falschen Zertifikats von einer vertrauenswürdigen CA ist.

Wichtig ist, dass dieser Schutzmechanismus tatsächlich nur für Verbindungen über das URL Loading System greift. Wenn Entwickler nicht die Standard-APIs verwenden, sondern über Low-Level-Methoden wie das Network Framework, CFNetwork oder Sockets eigene Lösungen bauen, greifen die Einschränkungen nicht.

Pinning-Varianten: Leaf, Intermediate oder Root

Certificate oder Public Key Pinning kann auf verschiedenen Ebenen ansetzen. Eine App könnte direkt das End-Zertifikat (Leaf Certificate) beziehungsweise den Schlüssel des Servers prüfen und nur eine Verbindung aufbauen, wenn der Server exakt den vorkonfigurierten Schlüssel zurückmeldet. Das kann aber ganz schnell zu Problemen führen, wenn sich der Schlüssel des Servers mal ändern sollte, weil er beispielsweise zurückgerufen oder ausgetauscht wurde. Dann kommt die App nicht mehr zu ihrem Backend durch.

Deutlich weniger fehleranfällig und wartungsärmer ist es daher, anstatt einzelner Server-Zertifikate nur die Zertifizierungsstelle vorzuschreiben. Dies geschieht, indem man übergeordnete Zertifikate innerhalb der Zertifikatskette wie bestimmte Intermediate- oder Root-Zertifikate einer CA anpinnt. Ein solcher Ansatz zeigt sich robuster gegenüber Zertifikatswechseln und stellt zumindest ­sicher, dass nur Zertifikate akzeptiert werden, die von der vorgeschriebenen Zertifizierungsstelle ausgestellt wurden. So erreicht man immerhin, nur der einen CA vertrauen zu müssen, nicht aber den hundert anderen im Trust Store. Für ­Enterprise-Apps ließe sich hier auch ohne Weiteres eine unternehmensinterne CA erzwingen.

Apropos Firmen-CA: In Firmennetzen kann Pinning zu Problemen führen, wenn ein Monitoring-Gerät sich aktiv in die Verbindung einklinkt, um eine TLS-gesicherte Verbindung zu inspizieren. Auch wenn dessen CA gültige Zertifikate ausstellt, lehnt das Pinning das untergeschobene MITM-Zertifikat ab und bricht die Verbindung ab. Fremde Apps, deren Pinning-Verhalten man nicht beeinflussen kann, verweigern in Unternehmensnetzen, in denen TLS/SSL-Monitoring erzwungen wird, daher möglicherweise den Dienst.

Pinning in der Praxis

Um eine Pinning-Konfiguration zu erzeugen, wird der öffentliche Schlüssel benötigt, den man anheften möchte. Im einfachsten Fall lässt sich ein Schlüssel aus dem zugehörigen X.509-Zertifikat über die ASN.1 Subject-Public-Key-Info-Struktur auslesen. Liegt der Schlüssel in binärer Kodierung vor (.der), wird darüber ein SHA256-Hashwert berechnet und zur Weiterverwendung Base64-kodiert.

Ein Beispiel: Im Fall der Domain heise.de signiert Let’s Encrypt die Zertifikate. Ein sicherheitsbewusster Entwickler möchte deshalb sicherstellen, dass eine App nur noch dann eine Verbindung zu heise.de aufbaut, wenn Let’s Encrypt das vom Server präsentierte Zertifikat ausgestellt hat.

Die Zertifikatsübersichtsseite von Let’s Encrypt erklärt, dass alle Server-­Zertifikate über das „Let’s Encrypt R3“ Intermediate-Zertifikat ausgestellt werden. Dieses Intermediate-Zertifikat beziehungsweise den darin enthaltenen Schlüssel kann man also anpinnen. Dazu lädt man das R3-Zertifikat im PEM-F­ormat herunter, extrahiert daraus den­ öffentlichen Schlüssel und berechnet ­dessen SHA-256-Hash-Wert. Das Kommandozeilenwerkzeug openssl hilft da­bei:

curl -s -O https://letsencrypt.org/ certs/lets-encrypt-r3.pem
cat lets-encrypt-r3.pem | openssl x509 -inform pem -noout -outform pem -pubkey | openssl pkey -pubin -inform pem -outform der | openssl dgst -sha256 -binary | openssl enc -base64 

Heraus kommt ein Hash wie jQJTbIh0grw0/1TkHSumWb+Fs0Ggogr621gT3PvPKG0=. Zukünftig will Let’s Encrypt seine Zertifikate über ein neues ECDSA-Intermediate-Zertifikat mit der Bezeichnung „Let’s Encrypt E1“ ausstellen. Das liefert analog den SHA-256-Hash J2/oqMTsdhFWW/n85tys6b4yDBtb6idZayIEBx7QTxA=, welcher gleich mit in die Liste der erlaubten CA-Schlüssel kommt.

Die so ermittelten Hash-Werte im ­Base64-Format kann man direkt in eine Pinning-Konfiguration übernehmen. Das geschieht innerhalb der Konfigurationsdatei Info.plist der App über einen NSAppTransportSecurity-Eintrag mit der Option NSPinnedDomains. Die Option NSPinnedCAIdentities legt schließlich fest, dass in der vom Server zurückgemeldeten Zertifikatskette für die Domain heise.de einer der beiden Let’s-Encrypt-Schlüssel enthalten sein muss (siehe oben links).

Eine auf diese Weise gesicherte App baut zu heise.de nur noch dann eine Verbindung auf, wenn Let’s Encrypt das vom Server gelieferte Zertifikat ausgestellt hat. Etwaige Zertifikats- oder Schlüsselwechsel beeinträchtigen die Funktionsfähigkeit der App aber nicht, solange Let’s Encrypt die Zertifikate weiterhin ausstellt und seitens der CA keine größeren Umstellungen anstehen.

Im Fall einer fehlgeschlagenen Zertifikatsvalidierung meldet iOS den Umstand mit dem Fehler SecureConnectionFailed an die App, standardmäßig würde diese aber keinen Warnhinweis anzeigen. Daher ist es wichtig, dass Entwickler in ihrer App selbst eine Fehlerbehandlung durch­führen, um auf etwaige Probleme beim Herstellen einer sicheren Verbindung zu reagieren oder dem Nutzer zumindest eine aufschlussreiche Fehlermeldung anzuzeigen.

Vorsicht bei Subdomains

Über den Konfigurationsschalter NS­Includes­Subdomains könnte ein einmal angehefteter Schlüssel für alle Subdomains einer Domain erzwungen werden. Das ist allerdings mit Vorsicht zu genießen: heise.de verwendet zwar Zertifikate von Let’s Encrypt. HTTP-Anfragen an https://heise.de beantwortet der Server aber mit einem Redirect auf https://www.heise.de. Und diese Domain verwendet keine Let’s-Encrypt-Zertifikate, sondern setzt auf die Sectigo-CA.

Schriebe man also in der Pinning-­Konfiguration einer App nun Let’s Encrypt via NSIncludeSubdomains auch für alle ­Subdomains fest, würde der Verbindungsaufbau bei der Weiterleitung auf www.heise.de abbrechen. Der Entwickler muss also ganz genau prüfen, mit welchen Systemen eine App kommuniziert und ob alle Systeme tatsächlich mit Zertifikaten der gleichen CA ausgestattet sind. ­Andernfalls sollte man besser für jede Domain die Schlüssel separat konfigurieren.

Risiken und Nebenwirkungen

Pinning stellt ein Problem dar, wenn Schlüssel oder Zertifikate regelmäßig rotieren. Die App Transport Security (ATS) bietet über die Option NSPinnedLeaf­Identities zwar auch die Möglichkeit, Server-Zertifikate festzunageln, aber das sollte man besser nur tun, wenn man sich seiner Schlüsselverwaltung extrem sicher ist (und vorsichtshalber auch gleich einen oder besser zwei Reserveschlüssel einbezieht).

Auch bei gemanagten Cloud-Lösungen ist Vorsicht geboten, da man beispielsweise bei gebrauchsfertigen API-Gateways die Schlüssel meist nicht selbst im Griff hat. So tauschte Microsoft erst im letzten Jahr die Root-CA-Zertifikate für diverse Azure-Dienste aus und forderte Entwickler auf, mögliche Pinning-Routinen zeitnah anzupassen.

Es ist nicht notwendig, dass der Entwickler auf XML-Code schaut, dafür haben die Apple-Entwickler einen einfachen Editor für XCode gebaut.

Im Browser-Umfeld setzte sich das als HTTPS Public Key Pinning (HPKP) bekannte Verfahren daher nicht durch. Zu groß war die Gefahr fehlerhafter Konfigurationen, durch die Webseitenbetreiber ihre Besucher fast unwiderruflich aussperrten. Mit Chrome 67 verabschiedete sich Google dann vollends von HPKP und somit war Zertifikats-Pinning für Webseiten faktisch beendet.

Bei Apps ist Pinning trotzdem weiter auf dem Vormarsch, denn die Risiken sind hier etwas besser im Zaum zu halten: Zum einen kontrolliert ein Anbieter mit App und Backend häufig beide Endpunkte und zum anderen, und das ist ein entscheidender Vorteil, existiert für das Pinning in Apps ein unabhängiger Recovery-Kanal. Bei HPKP wurden die Pins per HTTP Response Header ausgegeben und im Browser gespeichert. Eine Fehlkonfiguration oder ein Zertifikatswechsel hatte zur Folge, dass der einzig existierende Update-Pfad zwischen Browser und Website abgeschnitten war und Browser dann nicht mehr mit den neuen Zertifikaten versorgt werden konnten. Für Apps steht mit dem App Store hingegen ein unabhängiger Update-Kanal zur Verfügung, über den man im Notfall ein App-Update mit einer neuen Konfiguration verteilen kann.

Backup-Strategie einplanen

Wenn man sich dazu entscheidet, Pinning umzusetzen, sollte man sich also vorab Gedanken über eine Langzeitstrategie machen. Grundsätzlich empfehlenswert ist es immer, mehrere Schlüssel anzuheften, vor allem einen Notfall-Schlüssel, der für eine Backup-Verbindung zum Server kurzfristig aktiviert werden kann. Wenn alle Stricke reißen, hilft als letzter Ausweg nur noch ein Update der App über den Store, um so eine neue Pinning-Konfiguration auszurollen. Für diesen Fall sollte noch ein „ungepinnter“ Rückkanal zum Benutzer existieren, um auf das im Store verfügbare Update hinzuweisen.

Aber Vorsicht: Eine eigene Zertifikatsvalidierung zu implementieren ist ein komplexes Unterfangen und fehlerträchtig. Damit der Schuss am Ende nicht nach ­hinten losgeht, sollte man gemäß dem Mantra „Don’t Roll Your Own Security“ besser auf die neuen Pinning-Funktionen der App Transport Security zurückzugreifen, die Apple mit iOS 14 endlich eingeführt hat. (wid@ct.de)

Apple-Dokumentation für Entwickler: ct.de/yssq

Kommentieren