Sécurité avec les protocoles réseau

Les interactions chiffrées client-serveur utilisent le protocole TLS (Transport Layer Security) afin de protéger les données de votre application.

Cet article présente les bonnes pratiques liées au protocole réseau sécurisé et les considérations relatives à l'infrastructure à clé publique (PKI). Pour en savoir plus, consultez Vue d'ensemble de la sécurité Android et Présentation des autorisations.

Concepts

Un serveur doté d'un certificat TLS possède une clé publique et une clé privée correspondante. Le serveur signe son certificat lors du handshake TLS à l'aide de la cryptographie à clé publique.

Un simple handshake prouve seulement que le serveur connaît la clé privée du certificat. Pour résoudre ce problème, laissez le client approuver plusieurs certificats. Un serveur donné est considéré comme non fiable si son certificat n'apparaît pas dans l'ensemble de certificats approuvés côté client.

Toutefois, les serveurs peuvent utiliser la rotation des clés pour remplacer la clé publique de leur certificat par une nouvelle. La modification de la configuration du serveur nécessite la mise à jour de l'application cliente. S'il s'agit d'un service Web tiers, tel qu'un navigateur Web ou une application de messagerie, il est plus difficile de savoir quand mettre à jour l'application cliente.

Les serveurs s'appuient généralement sur des certificats d'autorités de certification pour délivrer des certificats, ce qui renforce la stabilité de la configuration côté client au fil du temps. Une autorité de certification signe un certificat de serveur à l'aide de sa clé privée. Le client peut ensuite vérifier que le serveur dispose d'un certificat CA connu de la plate-forme.

Les autorités de certification approuvées sont généralement répertoriées sur la plate-forme hôte. Android 8.0 (niveau d'API 26) comprend plus de 100 autorités de certification qui sont mises à jour dans chaque version et ne changent pas d'un appareil à l'autre.

Les applications clientes nécessitent un mécanisme de vérification du serveur, car l'autorité de certification propose des certificats pour de nombreux serveurs. Le certificat de l'autorité de certification identifie le serveur sous un nom spécifique, tel que gmail.com, ou à l'aide d'un caractère générique, comme *.google.com.

Pour afficher les informations de certificat de serveur d'un site Web, utilisez la commande s_client de l'outil openssl en indiquant le numéro de port. Par défaut, HTTPS utilise le port 443.

La commande transmet la sortie openssl s_client à openssl x509, qui met en forme les informations de certificat selon la norme X.509. La commande demande le sujet (nom du serveur) et l'émetteur (autorité de certification).

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

Exemple HTTPS

En supposant que vous disposez d'un serveur Web avec un certificat délivré par une autorité de certification connue, vous pouvez effectuer une requête sécurisée, comme indiqué dans le code suivant :

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);

Pour personnaliser les requêtes HTTP, diffusez sur HttpURLConnection. La documentation HttpURLConnection Android comprend des exemples pour la gestion des en-têtes de requête et de réponse, la publication de contenu, la gestion des cookies, l'utilisation de proxys, la mise en cache des réponses, etc. Le framework Android vérifie les certificats et les noms d'hôte à l'aide de ces API.

Utilisez ces API autant que possible. La section suivante traite des problèmes courants nécessitant différentes solutions.

Problèmes courants lors de la vérification des certificats de serveur

Supposons qu'au lieu de renvoyer du contenu, getInputStream() génère une exception :

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)

Plusieurs raisons peuvent expliquer cette situation. Par exemple :

  1. L'autorité de certification qui a délivré le certificat du serveur était inconnue.
  2. Le certificat du serveur n'a pas été signé par une autorité de certification, mais autosigné.
  3. Il manque une autorité de certification intermédiaire dans la configuration du serveur.

Les sections suivantes expliquent comment résoudre ces problèmes tout en préservant la sécurité de votre connexion au serveur.

Autorité de certification inconnue

L'exception SSLHandshakeException se produit car le système ne fait pas confiance à l'autorité de certification. Cela peut être dû au fait que vous disposez d'un certificat provenant d'une nouvelle autorité de certification non approuvée par Android ou que votre application fonctionne sur une version antérieure sans l'autorité de certification. Comme elle est privée, une autorité de certification est rarement connue. Le plus souvent, une autorité de certification est inconnue, car il ne s'agit pas d'une autorité de certification publique, mais plutôt privée, émise par une organisation telle qu'un gouvernement, une entreprise ou un établissement d'enseignement pour son propre usage.

Pour approuver des autorisations de certification personnalisées sans avoir à modifier le code de votre application, modifiez votre configuration de sécurité réseau.

Attention : De nombreux sites Web décrivent une mauvaise solution, qui consiste à installer un TrustManager qui n'a aucun effet. Cela rend vos utilisateurs vulnérables aux attaques lorsqu'ils utilisent un point d'accès Wi-Fi public, car un pirate informatique peut utiliser des astuces DNS pour envoyer le trafic de vos utilisateurs via un proxy qui se fait passer pour votre serveur. Le pirate informatique peut alors enregistrer des mots de passe et d’autres données personnelles. Cette approche est efficace car le pirate informatique peut générer un certificat, et sans TrustManager confirmant que le certificat provient d'une source fiable, vous ne pouvez pas bloquer ce type d'attaque. Alors ne le faites pas, même temporairement. À la place, faites en sorte que l'émetteur du certificat du serveur soit approuvé par votre application.

Certificat de serveur autosigné

Deuxièmement, l'exception SSLHandshakeException peut se produire en raison d'un certificat autosigné, faisant du serveur sa propre autorité de certification. Cette situation ressemble à celle d'une autorité de certification inconnue. Vous devez donc modifier la configuration de sécurité réseau de votre application pour approuver vos certificats autosignés.

Autorité de certification intermédiaire manquante

Troisièmement, l'exception SSLHandshakeException se produit en raison de l'absence d'une autorité de certification intermédiaire. Les autorités de certification publiques signent rarement les certificats de serveur. À la place, l'autorité de certification racine signe des autorités de certification intermédiaires.

Pour réduire le risque de compromission, les autorités de certification maintiennent l'autorité de certification racine hors connexion. Toutefois, les systèmes d'exploitation tels qu'Android ne font généralement confiance qu'aux autorités de certification racine directement, ce qui laisse un faible écart de confiance entre le certificat du serveur (signé par l'autorité de certification intermédiaire) et l'outil de vérification des certificats, qui reconnaît l'autorité de certification racine.

Pour éliminer cet écart de confiance, le serveur envoie une chaîne de certificats depuis l'autorité de certification du serveur par le biais de tout intermédiaire à une autorité de certification racine approuvée lors du handshake TLS.

Par exemple, voici la chaîne de certificats mail.google.com telle qu'elle est affichée par la commande openssl s_client :

$ 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
---

Cela montre que le serveur envoie un certificat pour mail.google.com délivré par l'autorité de certification Thawte SGC, une autorité de certification intermédiaire, et un deuxième certificat pour l'autorité de certification Thawte SGC délivré par une autorité de certification Verisign, l’autorité de certification principale approuvée par Android.

Toutefois, un serveur peut ne pas être configuré pour inclure l'autorité de certification intermédiaire nécessaire. Voici par exemple un serveur qui peut engendrer une erreur dans les navigateurs Android et des exceptions dans les applications Android :

$ 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
---

Contrairement à une autorité de certification inconnue ou à un certificat de serveur autosigné, la plupart des navigateurs pour ordinateur ne génèrent pas d'erreur lors de la communication avec ce serveur. Les navigateurs pour ordinateur mettent en cache les autorités de certification intermédiaires approuvées. Après avoir découvert une autorité de certification intermédiaire sur un site, le navigateur n'en aura plus besoin dans la chaîne de certificats.

Certains sites le font intentionnellement pour les serveurs Web secondaires qui diffusent des ressources. Pour économiser de la bande passante, ils peuvent diffuser leur page HTML principale à partir d'un serveur doté d'une chaîne de certificats complète, mais leurs images, CSS et JavaScript sans l'autorité de certification. Malheureusement, ces serveurs peuvent parfois fournir un service Web auquel vous essayez d'accéder depuis votre application Android, qui n'est pas aussi tolérante.

Pour résoudre ce problème, configurez le serveur pour inclure l'autorité de certification intermédiaire dans la chaîne du serveur. La plupart des autorités de certification fournissent des instructions sur la façon de procéder pour les serveurs Web courants.

Avertissements concernant l'utilisation directe de SSLSocket

Jusqu'à présent, les exemples se sont concentrés sur le protocole HTTPS avec HttpsURLConnection. Parfois, les applications doivent utiliser TLS séparément du protocole HTTPS. Par exemple, une application de messagerie peut utiliser des variantes TLS de SMTP, POP3 ou IMAP. Dans ce cas, l'application peut utiliser SSLSocket directement, de la même manière que HttpsURLConnection en interne.

Les techniques décrites jusqu'ici pour traiter les problèmes de vérification des certificats s'appliquent également à SSLSocket. En fait, lorsque vous utilisez un TrustManager personnalisé, ce qui est transmis à HttpsURLConnection est un élément SSLSocketFactory. Par conséquent, si vous devez utiliser un TrustManager personnalisé avec un SSLSocket, suivez la même procédure et utilisez cet élément SSLSocketFactory pour créer votre SSLSocket.

Attention : SSLSocket ne vérifie pas le nom d'hôte. Il appartient à votre application de vérifier elle-même le nom d'hôte, de préférence en appelant getDefaultHostnameVerifier() avec le nom d'hôte attendu. Sachez également que HostnameVerifier.verify() ne génère pas d'exception en cas d'erreur. À la place, il renvoie un résultat booléen que vous devez vérifier explicitement.

Autorités de certification bloquées

TLS s'appuie sur les autorités de certification pour délivrer des certificats uniquement aux propriétaires confirmés de serveurs et de domaines. Dans de rares cas, les autorités de certification sont piégées ou, dans le cas de Comodo ou de DigiNotar, compromises, ce qui entraîne la délivrance de certificats pour un nom d'hôte à une personne autre que le propriétaire du serveur ou du domaine.

Pour atténuer ce risque, Android peut ajouter certains certificats, voire des autorités de certification entières, à une liste de blocage. Bien que cette liste ait été historiquement intégrée au système d'exploitation, à partir d'Android 4.2, elle peut être mise à jour à distance pour gérer les futures compromissions.

Limiter votre application à des certificats spécifiques

Attention : L'épinglage de certificats (qui consiste à limiter les certificats considérés comme valides pour votre application à ceux que vous avez précédemment autorisés) n'est pas recommandé pour les applications Android. Les futurs changements de configuration du serveur, tels que le passage à une autre autorité de certification, rendent les applications avec des certificats épinglés incapables de se connecter au serveur sans recevoir une mise à jour logicielle du client.

Si vous souhaitez que votre application n'accepte que les certificats que vous spécifiez, vous devez impérativement inclure plusieurs épingles de sauvegarde, dont au moins une clé que vous contrôlez entièrement, et un délai d'expiration suffisamment court pour éviter les problèmes de compatibilité. La fonctionnalité Configuration de sécurité réseau prend en charge l'épinglage avec ces caractéristiques.

Certificats client

Cet article porte sur l'utilisation du protocole TLS pour sécuriser les communications avec les serveurs. TLS prend également en charge les certificats client, qui permettent au serveur de valider l'identité d'un client. Bien que celles-ci dépassent le cadre de cet article, les techniques à mettre en œuvre sont semblables à la spécification d'un TrustManager personnalisé.

Nogotofail : un outil de test de la sécurité du trafic réseau

Nogotofail est un outil qui vous permet de vérifier facilement que vos applications sont protégées contre les failles et erreurs de configuration connues de TLS/SSL. Il s'agit d'un outil automatisé, puissant et évolutif qui permet de tester les problèmes de sécurité réseau sur n'importe quel appareil dont le trafic réseau peut être traversé.

Nogotofail est utile pour trois cas d'utilisation principaux :

  • Rechercher des bugs et des failles
  • Vérifier les correctifs et surveiller les régressions
  • Comprendre quelles applications et quels appareils génèrent quel trafic

Nogotofail est compatible avec Android, iOS, Linux, Windows, ChromeOS et macOS, ainsi qu'avec tous les appareils que vous utilisez pour vous connecter à Internet. Un client est disponible pour configurer les paramètres et recevoir des notifications sur Android et Linux. Le moteur d'attaque lui-même peut être déployé en tant que routeur, serveur VPN ou proxy.

Vous pouvez accéder à l'outil depuis le projet Open Source Nogotofail.

Mises à jour des protocoles SSL et TLS

Android 10

Certains navigateurs, tels que Google Chrome, permettent aux utilisateurs de choisir un certificat lorsqu'un serveur TLS envoie un message de demande de certificat dans le cadre d'un handshake TLS. À partir d'Android 10, les objets KeyChain respectent les émetteurs et les paramètres de spécification de clé lors de l'appel de KeyChain.choosePrivateKeyAlias() pour afficher une invite de sélection de certificat aux utilisateurs. Il convient de noter que cette invite ne contient pas d'options non conformes aux spécifications du serveur.

Si aucun certificat sélectionnable par l'utilisateur n'est disponible (par exemple, si aucun certificat ne correspond aux spécifications du serveur ou si un appareil ne dispose d'aucun certificat installé), l'invite de sélection de certificat ne s'affiche pas du tout.

De plus, il n'est pas nécessaire sous Android 10 ou version ultérieure de verrouiller l'écran de l'appareil pour importer des clés ou des certificats CA dans un objet KeyChain.

TLS 1.3 activé par défaut

Sur Android 10 ou version ultérieure, TLS 1.3 est activé par défaut pour toutes les connexions TLS. Voici quelques détails importants sur notre mise en œuvre de TLS 1.3 :

  • Les suites de chiffrement TLS 1.3 ne peuvent pas être personnalisées. Les suites de chiffrement TLS 1.3 prises en charge sont toujours activées lorsque que TLS 1.3 est activé. Toute tentative de désactivation en appelant setEnabledCipherSuites() est ignorée.
  • Lorsque TLS 1.3 est négocié, les objets HandshakeCompletedListener sont appelés avant que les sessions ne soient ajoutées au cache de session. (Dans TLS 1.2 et d'autres versions antérieures, ces objets sont appelés après l'ajout des sessions au cache de session.)
  • Dans certains cas où les instances SSLEngine génèrent une exception SSLHandshakeException sur des versions antérieures d'Android, elles génèrent une exception SSLProtocolException sur Android 10 ou version ultérieure.
  • Le mode 0-RTT n'est pas pris en charge.

Si vous le souhaitez, vous pouvez obtenir un élément SSLContext pour lequel TLS 1.3 est désactivé en appelant SSLContext.getInstance("TLSv1.2"). Vous pouvez également activer ou désactiver des versions de protocole pour chaque connexion en appelant setEnabledProtocols() sur un objet approprié.

Les certificats signés avec SHA-1 ne sont pas approuvés dans TLS

Dans Android 10, les certificats qui utilisent l'algorithme de hachage SHA-1 ne sont pas approuvés dans les connexions TLS. Les autorités de certification racine n'ont pas délivré de certificats de ce type depuis 2016 et ne sont plus approuvées dans Chrome ni dans les autres principaux navigateurs.

Toute tentative de connexion échoue si elle est destinée à un site qui présente un certificat SHA-1.

Modifications et améliorations du comportement de KeyChain

Certains navigateurs, tels que Google Chrome, permettent aux utilisateurs de choisir un certificat lorsqu'un serveur TLS envoie un message de demande de certificat dans le cadre d'un handshake TLS. À partir d'Android 10, les objets KeyChain respectent les émetteurs et les paramètres de spécification de clé lors de l'appel de KeyChain.choosePrivateKeyAlias() pour afficher une invite de sélection de certificat aux utilisateurs. Il convient de noter que cette invite ne contient pas d'options non conformes aux spécifications du serveur.

Si aucun certificat sélectionnable par l'utilisateur n'est disponible (par exemple, si aucun certificat ne correspond aux spécifications du serveur ou si un appareil ne dispose d'aucun certificat installé), l'invite de sélection de certificat ne s'affiche pas du tout.

De plus, il n'est pas nécessaire sous Android 10 ou version ultérieure de verrouiller l'écran de l'appareil pour importer des clés ou des certificats CA dans un objet KeyChain.

Autres modifications apportées au protocole TLS et à la cryptographie

Plusieurs modifications mineures ont été apportées aux bibliothèques TLS et de cryptographie dans Android 10 :

  • Les algorithmes de chiffrement AES/GCM/NoPadding et ChaCha20/Poly1305/NoPadding renvoient des tailles de mémoire tampon plus précises à partir de getOutputSize().
  • La suite de chiffrement TLS_FALLBACK_SCSV est omise pour les tentatives de connexion avec un protocole maximal TLS 1.2 ou supérieur. En raison des améliorations apportées aux mises en œuvre des serveurs TLS, nous vous déconseillons de tenter un remplacement externe TLS. Nous vous recommandons plutôt de vous fier à la négociation de version TLS.
  • ChaCha20-Poly1305 est un alias de ChaCha20/Poly1305/NoPadding.
  • Les noms d'hôte se terminant par un point ne sont pas considérés comme des noms d'hôte SNI valides.
  • L'extension supported_signature_algorithms de CertificateRequest est respectée lors du choix d'une clé de signature pour les réponses de certificat.
  • Les clés de signature opaques, comme celles d'Android Keystore, peuvent être utilisées avec des signatures RSA-PSS dans TLS.

Modifications des connexions HTTPS

Si une application exécutant Android 10 transmet une valeur nulle à setSSLSocketFactory(), une exception IllegalArgumentException se produit. Dans les versions précédentes, transmettre une valeur nulle à setSSLSocketFactory() avait le même effet que transmettre la fabrique par défaut actuelle.

Android 11

Les sockets SSL utilisent le moteur SSL Conscrypt par défaut

L'implémentation SSLSocket par défaut d'Android est basée sur Conscrypt. Depuis Android 11, cette implémentation est basée en interne sur SSLEngine de Conscrypt.