Sicherheit mit Netzwerkprotokollen

Bei client-server-verschlüsselten Interaktionen werden die Daten Ihrer App mit Transport Layer Security (TLS) geschützt.

In diesem Artikel werden Best Practices für sichere Netzwerkprotokolle und Public-Key-Infrastruktur (PKI) erläutert. Weitere Informationen finden Sie unter Android-Sicherheit – Übersicht und Berechtigungen – Übersicht.

Concepts

Ein Server mit einem TLS-Zertifikat hat einen öffentlichen Schlüssel und einen zugehörigen privaten Schlüssel. Der Server verwendet die Public-Key-Kryptografie, um sein Zertifikat während des TLS-Handshakes zu signieren.

Ein einfacher Handshake beweist nur, dass der Server den privaten Schlüssel des Zertifikats kennt. Um dieses Problem zu beheben, sollten Sie dem Client mehrere Zertifikate vertrauen lassen. Ein bestimmter Server ist nicht vertrauenswürdig, wenn sein Zertifikat nicht in der clientseitigen Gruppe vertrauenswürdiger Zertifikate enthalten ist.

Server können jedoch die Schlüsselrotation verwenden, um den öffentlichen Schlüssel ihres Zertifikats durch einen neuen zu ersetzen. Die Änderung der Serverkonfiguration erfordert die Aktualisierung der Client-App. Wenn es sich bei dem Server um einen Webdienst eines Drittanbieters handelt, z. B. einen Webbrowser oder eine E-Mail-App, ist es schwieriger zu erkennen, wann die Client-App aktualisiert werden muss.

Für die Ausstellung von Zertifikaten auf Servern werden in der Regel Zertifizierungsstellenzertifikate (CA-Zertifikate) verwendet. Dadurch bleibt die clientseitige Konfiguration im Laufe der Zeit stabiler. Eine Zertifizierungsstelle signiert ein Serverzertifikat mit ihrem privaten Schlüssel. Der Client kann dann prüfen, ob der Server ein von der Plattform bekanntes CA-Zertifikat hat.

Vertrauenswürdige Zertifizierungsstellen sind in der Regel auf der Hostplattform aufgeführt. Android 8.0 (API-Ebene 26) enthält über 100 CAs, die in jeder Version aktualisiert werden und sich nicht zwischen Geräten ändern.

Client-Apps benötigen einen Mechanismus zur Überprüfung des Servers, da die Zertifizierungsstelle Zertifikate für zahlreiche Server anbietet. Das Zertifikat der Zertifizierungsstelle identifiziert den Server entweder mit einem bestimmten Namen wie gmail.com oder mit einem Platzhalter wie *.google.com.

Wenn Sie die Serverzertifikatsinformationen einer Website aufrufen möchten, verwenden Sie den Befehl s_client des Tools openssl und geben Sie die Portnummer an. Standardmäßig wird für HTTPS Port 443 verwendet.

Der Befehl überträgt die openssl s_client-Ausgabe an openssl x509, das die Zertifikatsinformationen im X.509-Standard formatiert. Der Befehl fordert das Thema (Servername) und den Aussteller (Zertifizierungsstelle) an.

openssl s_client -connect WEBSITE-URL:443 | \
  openssl x509 -noout -subject -issuer

Ein HTTPS-Beispiel

Angenommen, Sie haben einen Webserver mit einem Zertifikat, das von einer bekannten Zertifizierungsstelle ausgestellt wurde. Sie können dann eine sichere Anfrage stellen, wie im folgenden Code gezeigt:

Kotlin

val url = URL("https://wikipedia.org")
val urlConnection: URLConnection = url.openConnection()
val inputStream: InputStream = urlConnection.getInputStream()
copyInputStreamToOutputStream(inputStream, System.out)

Java

URL url = new URL("https://wikipedia.org");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

Wenn Sie HTTP-Anfragen anpassen möchten, führen Sie einen Cast auf HttpURLConnection aus. Die Android-HttpURLConnection-Dokumentation enthält unter anderem Beispiele für die Verarbeitung von Anfrage- und Antwortheadern, das Veröffentlichen von Inhalten, das Verwalten von Cookies, die Verwendung von Proxys und das Caching von Antworten. Das Android-Framework überprüft Zertifikate und Hostnamen mithilfe dieser APIs.

Verwenden Sie nach Möglichkeit diese APIs. Im folgenden Abschnitt werden häufige Probleme beschrieben, für die unterschiedliche Lösungen erforderlich sind.

Häufige Probleme bei der Überprüfung von Serverzertifikaten

Angenommen, anstelle von Inhalten gibt getInputStream() eine Ausnahme zurück:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
        at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)

Dafür kann es verschiedene Gründe geben:

  1. Die Zertifizierungsstelle, die das Serverzertifikat ausgestellt hat, war unbekannt.
  2. Das Serverzertifikat wurde nicht von einer Zertifizierungsstelle, sondern selbst signiert.
  3. In der Serverkonfiguration fehlt eine Zwischen-CA.

In den folgenden Abschnitten wird beschrieben, wie Sie diese Probleme beheben und gleichzeitig die Verbindung zum Server schützen.

Unbekannte Zertifizierungsstelle

Der Fehler SSLHandshakeException tritt auf, weil das System der Zertifizierungsstelle nicht vertraut. Das kann daran liegen, dass Sie ein Zertifikat von einer neuen Zertifizierungsstelle haben, der Android nicht vertraut, oder dass Ihre App mit einer früheren Version ohne die Zertifizierungsstelle ausgeführt wird. Da sie privat ist, ist eine CA selten bekannt. Meistens ist eine Zertifizierungsstelle unbekannt, weil es sich nicht um eine öffentliche Zertifizierungsstelle handelt, sondern um eine private, die von einer Organisation wie einer Behörde, einem Unternehmen oder einer Bildungseinrichtung für den eigenen Gebrauch ausgestellt wurde.

Wenn Sie benutzerdefinierten Zertifizierungsstellen vertrauen möchten, ohne den Code Ihrer App ändern zu müssen, ändern Sie die Netzwerksicherheitskonfiguration.

Achtung:Auf vielen Websites wird eine schlechte Alternative beschrieben, nämlich die Installation einer TrustManager, die nichts tut. Dadurch sind Ihre Nutzer anfällig für Angriffe, wenn sie einen öffentlichen WLAN-Hotspot nutzen. Angreifer können mit DNS-Tricks den Traffic Ihrer Nutzer über einen Proxy senden, der vorgibt, Ihr Server zu sein. Der Angreifer kann dann Passwörter und andere personenbezogene Daten aufzeichnen. Das funktioniert, weil der Angreifer ein Zertifikat generieren kann. Ohne TrustManager, das bestätigt, dass das Zertifikat aus einer vertrauenswürdigen Quelle stammt, können Sie diese Art von Angriff nicht blockieren. Tun Sie das also nicht, auch nicht vorübergehend. Stattdessen sollte Ihre App dem Aussteller des Serverzertifikats vertrauen.

Selbst signiertes Serverzertifikat

Zweitens kann SSLHandshakeException aufgrund eines selbst signierten Zertifikats auftreten, wodurch der Server seine eigene Zertifizierungsstelle ist. Das ist vergleichbar mit einer unbekannten Zertifizierungsstelle. Ändern Sie daher die Netzwerksicherheitskonfiguration Ihrer Anwendung, damit Ihren selbstsignierten Zertifikaten vertraut wird.

Fehlende Zwischenzertifizierungsstelle

Drittens: SSLHandshakeException tritt aufgrund einer fehlenden Zwischen-CA auf. Öffentliche Zertifizierungsstellen signieren nur selten Serverzertifikate. Stattdessen signiert die Stamm-CA Zwischen-CAs.

Um das Risiko von Manipulationen zu verringern, halten Zertifizierungsstellen die Stamm-CA offline. Betriebssysteme wie Android vertrauen jedoch normalerweise nur Stammzertifizierungsstellen direkt. Dadurch entsteht eine kurze Vertrauenslücke zwischen dem Serverzertifikat, das von der zwischengeschalteten Zertifizierungsstelle signiert wurde, und dem Zertifikatsprüfer, der die Stammzertifizierungsstelle erkennt.

Um diese Vertrauenslücke zu schließen, sendet der Server während des TLS-Handshakes eine Zertifikatskette von der Server-CA über alle Zwischenstellen an eine vertrauenswürdige Stamm-CA.

Hier ist beispielsweise die Zertifikatskette für mail.google.com, wie sie mit dem Befehl openssl s_client angezeigt wird:

$ openssl s_client -connect mail.google.com:443
---
Certificate chain
 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=mail.google.com
   i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
---

Das zeigt, dass der Server ein Zertifikat für mail.google.com sendet, das von der Thawte SGC CA ausgestellt wurde, einer Zwischen-CA, und ein zweites Zertifikat für die Thawte SGC CA, das von einer Verisign CA ausgestellt wurde, der primären CA, die von Android als vertrauenswürdig eingestuft wird.

Ein Server ist jedoch möglicherweise nicht so konfiguriert, dass die erforderliche Zwischen-CA enthalten ist. Hier ist beispielsweise ein Server, der in Android-Browsern zu einem Fehler und in Android-Apps zu Ausnahmen führen kann:

$ openssl s_client -connect egov.uscis.gov:443
---
Certificate chain
 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3
---

Im Gegensatz zu einer unbekannten Zertifizierungsstelle oder einem selbstsignierten Serverzertifikat werfen die meisten Desktop-Browser bei der Kommunikation mit diesem Server keinen Fehler aus. In Desktop-Browsern werden vertrauenswürdige Zwischenzertifizierungsstellen im Cache gespeichert. Nachdem ein Browser eine Zwischen-CA von einer Website erfahren hat, benötigt er sie nicht mehr in der Zertifikatskette.

Einige Websites tun dies absichtlich für sekundäre Webserver, die Ressourcen bereitstellen. Um Bandbreite zu sparen, kann die Haupt-HTML-Seite von einem Server mit einer vollständigen Zertifikatskette ausgeliefert werden, die Bilder, CSS und JavaScript jedoch ohne die Zertifizierungsstelle. Leider können diese Server gelegentlich einen Webdienst bereitstellen, den Sie über Ihre Android-App erreichen möchten, der nicht so tolerant ist.

Um dieses Problem zu beheben, konfigurieren Sie den Server so, dass die Zwischen-CA in die Serverkette aufgenommen wird. Die meisten Zertifizierungsstellen stellen Anleitungen für gängige Webserver zur Verfügung.

Warnungen zur direkten Verwendung von SSLSocket

Bisher haben sich die Beispiele auf HTTPS mit HttpsURLConnection konzentriert. Manchmal müssen Apps TLS getrennt von HTTPS verwenden. Eine E-Mail-App kann beispielsweise TLS-Varianten von SMTP, POP3 oder IMAP verwenden. In diesen Fällen kann die App SSLSocket direkt verwenden, ähnlich wie HttpsURLConnection intern verwendet wird.

Die bisher beschriebenen Methoden zur Behebung von Problemen mit der Zertifikatsüberprüfung gelten auch für SSLSocket. Bei Verwendung einer benutzerdefinierten TrustManager wird an HttpsURLConnection eine SSLSocketFactory übergeben. Wenn Sie also eine benutzerdefinierte TrustManager mit einer SSLSocket verwenden möchten, führen Sie dieselben Schritte aus und verwenden Sie diese SSLSocketFactory, um Ihre SSLSocket zu erstellen.

Achtung:SSLSocket führt keine Hostnamenüberprüfung durch. Ihre App muss die Hostnamenüberprüfung selbst durchführen, vorzugsweise durch Aufrufen von getDefaultHostnameVerifier() mit dem erwarteten Hostnamen. Beachte außerdem, dass HostnameVerifier.verify() bei einem Fehler keine Ausnahme auslöst. Stattdessen wird ein boolesches Ergebnis zurückgegeben, das Sie explizit prüfen müssen.

Zertifikatsvalidierung

Bei TLS werden Zertifikate nur von Zertifizierungsstellen an die bestätigten Inhaber von Servern und Domains ausgestellt. In seltenen Fällen werden Zertifizierungsstellen entweder ausgetrickst oder, wie im Fall von Comodo oder DigiNotar, gehackt, was dazu führt, dass die Zertifikate für einen Hostnamen an eine andere Person als den Inhaber des Servers oder der Domain ausgestellt werden.

Um dieses Risiko zu minimieren, verarbeitet Android den Zertifikatswiderruf systemweit über eine Kombination aus Sperrliste und Certificate Transparency, ohne sich auf die Online-Zertifikatsüberprüfung zu verlassen. Außerdem werden von Android OCSP-Antworten validiert, die an den TLS-Handshake angehängt sind.

Informationen zum Aktivieren der Zertifikattransparenz in Ihrer App finden Sie in der Dokumentation zur Netzwerksicherheitskonfiguration im Abschnitt Zertifikattransparenz aktivieren.

App auf bestimmte Zertifikate beschränken

Achtung:Die Zertifikatsbindung, bei der die für Ihre App gültigen Zertifikate auf die von Ihnen zuvor autorisierten beschränkt werden, wird für Android-Apps nicht empfohlen. Bei zukünftigen Änderungen an der Serverkonfiguration, z. B. bei einem Wechsel zu einer anderen Zertifizierungsstelle, können Apps mit angepinnten Zertifikaten nicht mehr ohne Clientsoftwareupdate eine Verbindung zum Server herstellen.

Wenn Sie Ihre App so einschränken möchten, dass nur von Ihnen angegebene Zertifikate akzeptiert werden, müssen Sie mehrere Sicherungs-PINs angeben, darunter mindestens einen Schlüssel, der vollständig in Ihrer Kontrolle steht, und eine ausreichend kurze Gültigkeitsdauer, um Kompatibilitätsprobleme zu vermeiden. Die Netzwerksicherheitskonfiguration bietet die Möglichkeit, diese Funktionen zu pinnen.

Clientzertifikate

In diesem Artikel geht es hauptsächlich um die Verwendung von TLS zur Absicherung der Kommunikation mit Servern. TLS unterstützt auch Clientzertifikate, mit denen der Server die Identität eines Clients überprüfen kann. Die entsprechenden Verfahren ähneln der Angabe einer benutzerdefinierten TrustManager, werden aber in diesem Artikel nicht behandelt.

Nogotofail: Ein Tool zum Testen der Sicherheit von Netzwerkverkehr

Nogotofail ist ein Tool, mit dem Sie ganz einfach prüfen können, ob Ihre Apps vor bekannten TLS/SSL-Sicherheitslücken und Fehlkonfigurationen geschützt sind. Es ist ein automatisiertes, leistungsstarkes und skalierbares Tool zum Testen von Netzwerksicherheitsproblemen auf jedem Gerät, dessen Netzwerkverkehr durch das Tool geleitet werden kann.

Nogotofail ist für drei Hauptanwendungsfälle nützlich:

  • Bugs und Sicherheitslücken finden
  • Fehlerbehebungen prüfen und auf Rückfälle achten
  • Sie können nachvollziehen, welche Anwendungen und Geräte welchen Traffic generieren.

Nogotofail funktioniert für Android, iOS, Linux, Windows, ChromeOS, macOS und jedes Gerät, mit dem Sie eine Internetverbindung herstellen. Ein Client ist verfügbar, um die Einstellungen zu konfigurieren und Benachrichtigungen unter Android und Linux zu erhalten. Die Angriffs-Engine selbst kann als Router, VPN-Server oder Proxy bereitgestellt werden.

Sie können über das Open-Source-Projekt Nogotofail auf das Tool zugreifen.

Updates für SSL und TLS

Android 10

In einigen Browsern wie Google Chrome können Nutzer ein Zertifikat auswählen, wenn ein TLS-Server im Rahmen eines TLS-Handshakes eine Zertifikatsanfrage sendet. Ab Android 10 werden bei KeyChain-Objekten die Aussteller und Schlüsselspezifikationsparameter berücksichtigt, wenn KeyChain.choosePrivateKeyAlias() aufgerufen wird, um Nutzern eine Aufforderung zur Zertifikatsauswahl anzuzeigen. Insbesondere enthält dieser Prompt keine Optionen, die nicht den Serverspezifikationen entsprechen.

Wenn keine vom Nutzer auswählbaren Zertifikate verfügbar sind, z. B. wenn keine Zertifikate der Serverspezifikation entsprechen oder auf einem Gerät keine Zertifikate installiert sind, wird die Aufforderung zur Zertifikatsauswahl nicht angezeigt.

Außerdem ist unter Android 10 oder höher keine Displaysperre erforderlich, um Schlüssel oder CA-Zertifikate in ein Keychain-Objekt zu importieren.

TLS 1.3 standardmäßig aktiviert

Unter Android 10 und höher ist TLS 1.3 standardmäßig für alle TLS-Verbindungen aktiviert. Hier sind einige wichtige Details zu unserer TLS 1.3-Implementierung:

  • Die TLS 1.3-Chiffrenfolgen können nicht angepasst werden. Die unterstützten TLS 1.3-Verschlüsselungssammlungen sind immer aktiviert, wenn TLS 1.3 aktiviert ist. Jeglicher Versuch, sie durch Aufrufen von setEnabledCipherSuites() zu deaktivieren, wird ignoriert.
  • Wenn TLS 1.3 ausgehandelt wird, werden HandshakeCompletedListener-Objekte aufgerufen, bevor Sitzungen dem Sitzungscache hinzugefügt werden. In TLS 1.2 und anderen früheren Versionen werden diese Objekte aufgerufen, nachdem Sitzungen dem Sitzungscache hinzugefügt wurden.
  • In einigen Fällen, in denen SSLEngine-Instanzen in früheren Android-Versionen eine SSLHandshakeException auswerfen, werfen diese Instanzen unter Android 10 und höher stattdessen eine SSLProtocolException aus.
  • Der 0-RTT-Modus wird nicht unterstützt.

Wenn Sie möchten, können Sie einen SSLContext mit deaktiviertem TLS 1.3 abrufen, indem Sie SSLContext.getInstance("TLSv1.2") aufrufen. Sie können Protokollversionen auch pro Verbindung aktivieren oder deaktivieren, indem Sie setEnabledProtocols() für ein entsprechendes Objekt aufrufen.

Mit SHA-1 signierte Zertifikate werden in TLS nicht als vertrauenswürdig eingestuft

In Android 10 werden Zertifikate, die den SHA-1-Hashalgorithmus verwenden, in TLS-Verbindungen nicht als vertrauenswürdig eingestuft. Root-Zertifizierungsstellen haben seit 2016 keine solchen Zertifikate mehr ausgestellt und sie werden in Chrome oder anderen gängigen Browsern nicht mehr als vertrauenswürdig eingestuft.

Alle Verbindungsversuche schlagen fehl, wenn die Verbindung zu einer Website besteht, die ein Zertifikat mit SHA-1 vorlegt.

Änderungen und Verbesserungen am Verhalten von KeyChain

In einigen Browsern wie Google Chrome können Nutzer ein Zertifikat auswählen, wenn ein TLS-Server im Rahmen eines TLS-Handshakes eine Zertifikatsanfrage sendet. Ab Android 10 werden bei KeyChain-Objekten die Aussteller- und Schlüsselspezifikationsparameter berücksichtigt, wenn KeyChain.choosePrivateKeyAlias() aufgerufen wird, um Nutzern eine Aufforderung zur Auswahl eines Zertifikats anzuzeigen. Insbesondere enthält dieser Prompt keine Optionen, die nicht den Serverspezifikationen entsprechen.

Wenn keine vom Nutzer auswählbaren Zertifikate verfügbar sind, z. B. wenn keine Zertifikate der Serverspezifikation entsprechen oder auf einem Gerät keine Zertifikate installiert sind, wird die Aufforderung zur Zertifikatsauswahl nicht angezeigt.

Außerdem ist unter Android 10 oder höher keine Displaysperre erforderlich, um Schlüssel oder CA-Zertifikate in ein Keychain-Objekt zu importieren.

Weitere Änderungen bei TLS und Kryptografie

Es gab mehrere kleinere Änderungen an den TLS- und Kryptografie-Bibliotheken, die unter Android 10 wirksam werden:

  • Die AES/GCM/NoPadding- und ChaCha20/Poly1305/NoPadding-Verschlüsselungen geben genauere Puffergrößen ab getOutputSize() zurück.
  • Die Chiffrensammlung TLS_FALLBACK_SCSV wird bei Verbindungsversuchen mit einem maximalen Protokoll von TLS 1.2 oder höher nicht berücksichtigt. Aufgrund von Verbesserungen bei der TLS-Serverimplementierung empfehlen wir, keinen externen TLS-Fallback zu versuchen. Stattdessen empfehlen wir die TLS-Versionenverhandlung.
  • ChaCha20-Poly1305 ist ein Alias für ChaCha20/Poly1305/NoPadding.
  • Hostnamen mit Endpunkten gelten nicht als gültige SNI-Hostnamen.
  • Die Erweiterung „supported_signature_algorithms“ in „CertificateRequest“ wird bei der Auswahl eines Signaturschlüssels für Zertifikatantworten berücksichtigt.
  • Opaque-Signaturschlüssel, z. B. aus dem Android Keystore, können mit RSA-PSS-Signaturen in TLS verwendet werden.

Änderungen an HTTPS-Verbindungen

Wenn eine App mit Android 10 „null“ an setSSLSocketFactory() übergibt, tritt ein IllegalArgumentException auf. In früheren Versionen hatte das Übergeben von „null“ an setSSLSocketFactory() denselben Effekt wie das Übergeben der aktuellen Standard-Factory.

Android 11

SSL-Sockets verwenden standardmäßig die Conscrypt-SSL-Engine

Die Standard-SSLSocket-Implementierung von Android basiert auf Conscrypt. Seit Android 11 wird diese Implementierung intern auf der SSLEngine von Conscrypt aufgebaut.