Pinning SSL avec TrustKit

Le pinning SSL « maison », c’est un peu compliqué parfois.

Heureusement, l’excellente équipe de Data Theorem est là.

Ils fournissent depuis plusieurs années la solution TrustKit sur iOS, tvOS, macOS et watchOS qui permet de mettre en place le pinning SSL en 2 temps 3 mouvements.

Ils fournissent maintenant une version Android de TrustKit compatible avec les API 15+.

Pour ceux qui ne connaissent pas le fonctionnement de TrustKit, c’est franchement simple.

En gros,  il faut déterminer ce que l’on va pinner dans la chaîne de confiance, à l’instar de HPKP :

  • le certificat de l’autorité de certification (CA)
  • le certificat de l’autorité de certification intermédiaire (ies)
  • le certificat du serveur (EE, leaf)

À partir de là, on peut calculer les clés publiques actuelles. Il est fortement conseillé d’utiliser une clé de secours. Et, enfin, on peut ajouter une date d’expiration.

En gros, si les clés sont validées, ça passe. Sinon, on regarde côté clé de secours. Et, passé la date d’expiration, on ne fait plus de pinning.

Et ensuite : TrustKit fait le job. Et ça, c’est top.

Pourquoi le pinning SSL ne suffit pas toujours

Pour rappel, le pinning SSL permet de sécuriser une connexion SSL afin d’éviter les attaques MITM.

Le truc, c’est que ça ne suffit pas pour protéger son code, même si cela évite la très grande majorité des attaques.

Pourquoi ?

Autant mettre ma source, cela ira plus vite : http://fr.slideshare.net/anantshri/ssl-pinning-and-bypasses-android-and-ios

En résumé, l’idée est :

  • sur iOS, de casser la sécurité de l’app chiffrée (via jailbreak, etc) et patcher l’app de sorte à modifier le comportement du SSL pour que tous les appels soient valides
  • sur Android, c’est le même procédé, sauf que l’app n’est pas chiffrée, ce qui simplifie grandement le travail.

Que faire ?

Pour les informations sensibles (mot de passe) : les chiffrer, de sortes à avoir un niveau de protection supplémentaire.

Mais si l’app est crackée, le chiffrage ne sert pas à grand-chose.

Sinon, sur iOS, le mieux est de bloquer – si possible – l’exécution de l’app sur un système jailbreaké.

Sur Android, c’est un peu plus compliqué à sécurisé.

Éventuellement, l’alternative est d’intégrer les appels SSL directement dans son code, via OpenSSL par exemple. Avec le NDK dans Android. Mais cela peut toujours être détourné.

Globalement, le problème vient du système d’exploitation. Il faut surtout espérer qu’une solution soit trouvée de ce côté-là.

En intégrant le pinning SSL directement dans le système d’exploitation, l’ensemble serait sécurisé de manière automatique.

Le pinning SSL : comment on fait ?

Le pinning SSL permet de s’assurer, lors d’une connexion HTTPS, que celui qui utilise un certificat SSL est bien celui qu’il prétend être. Sinon, on lui coupe le sifflet !

Le pinning SSL et ses outils

Pour faire du pinning SSL, il y a diverses stratégies. Inutile de s’étendre sur le sujet. OWASP a déjà rédigé un article bien complet là-dessus.

Même plusieurs articles.

Mais comment ils font les autres ?

C’est bien le coeur du problème. Connaître la technique est une chose. Savoir mettre en place la technique en est une autre.

Le pinning de clé publique, c’est assez cool, mais c’est un peu trickie comme approche et parfois complexe.

Le plus simple est certainement le pinning du certificat en lui-même.

Ha oui, mais si le certificat change ?

Une faille comme Heartbleed peut en effet forcer à révoquer les certificats.

Qui dit certificat changé dit pinning cassé.

Donc autant ne pas faire de pinning SSL ?

Chiffrer les données au-delà du HTTPS

L’avantage des technologies modernes, c’est qu’on peut chiffrer certaines données. Sans nécessairement passer par HTTPS.

Par exemple, on peut imaginer un serveur utilisé uniquement en cas d’incident. Par exemple, quand la clé enregistrée dans l’app ne fonctionne plus et qu’il faut en enregistrer une autre.

On effectue alors une requête précise sans pinning sur le service web. Avec un système de jeton unique qui ne peut être généré quand connaissant un algorithme particulier. Ce jeton inclue une clé valable temporairement partagée entre le client et le serveur. Cette clé permet d’obtenir une autre clé qui servira à chiffrer l’information avec encore une autre clé, également partagée entre client et serveur et d’une durée temporaire.

Le client reçoit l’information. La déchiffre avec la clé aléatoire qu’il a généré + la clé partagée. Là, il a la nouvelle empreinte permettant d’effectuer le pinning sur le nouveau certificat.

La clé partagée entre le client et le serveur sera chiffrée sur iOS. Aucun soucis.

Sur Android, la mauvaise idée est de la mettre dans les préférences. Ou de la mettre en dur dans le code. Heureusement, il y a le NDK qui permet de compiler des informations. Evidemment, ce code peut-être décompilé. Mais on peut aussi obfusquer le code du NDK. Et là, c’est moins cool à décompiler et comprendre. Surtout s’il y a en plus du fake code avec une bonne obfuscation du code Java en lui-même.

Bref. En attendant la mise à jour de l’app, c’est une solution de secours qui évite l’interruption de service.

Ça peut aussi être utile pour les identifiants d’accès aux services web externes (Facebook, Google, Cloudinary, etc).

Ha ouais, c’est cool

Bah ouais. Maintenant, faut s’y mettre. Avec un peu de bonne volonté, tout est possible.