ScaleRocket/Mobile

SSL Pinning

Certificate pinning pour les appels API — quand c'est nécessaire et comment le configurer dans React Native.

SSL Pinning

Le certificate pinning (SSL pinning) garantit que votre app communique uniquement avec votre serveur spécifique en vérifiant le certificat du serveur contre une copie connue, empêchant les attaques man-in-the-middle même sur des réseaux compromis.

Quand en avez-vous besoin

ScénarioSSL Pinning ?
App SaaS standardGénéralement pas nécessaire
App bancaire / financièreRequis
Santé / données médicalesRequis
App traitant des données gouvernementalesRequis
Entreprise avec exigences de conformitéProbablement nécessaire

Pour la plupart des apps, HTTPS avec une validation de certificat appropriée est suffisant. Le SSL pinning ajoute de la complexité et un surcoût de maintenance (vous devez mettre à jour l'app quand les certificats changent).

Comment ça marche

Sans pinning :

  1. L'app se connecte au serveur
  2. L'OS vérifie le certificat du serveur contre toute CA de confiance
  3. Connexion établie

Avec pinning :

  1. L'app se connecte au serveur
  2. L'app vérifie le certificat du serveur contre un certificat ou clé publique spécifique
  3. Si ça ne correspond pas, la connexion est rejetée

Cela empêche les attaques où une CA malveillante émet un faux certificat pour votre domaine.

Implémentation avec expo-network

Pour un pinning basique dans Expo, utilisez un wrapper fetch personnalisé :

import { Platform } from "react-native";

const PINNED_CERTIFICATES = {
  "api.example.com": "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
};

// Note : Le vrai certificate pinning nécessite des modules natifs
// Cet exemple montre le concept — utilisez une bibliothèque en production

Bibliothèque React Native SSL Pinning

Pour les apps en production, utilisez react-native-ssl-pinning :

npm install react-native-ssl-pinning
npx expo prebuild
import { fetch as pinnedFetch } from "react-native-ssl-pinning";

async function secureApiCall(endpoint: string) {
  const response = await pinnedFetch(`https://api.example.com${endpoint}`, {
    method: "GET",
    headers: { "Content-Type": "application/json" },
    sslPinning: {
      certs: ["my-server-cert"], // Nom du fichier de certificat sans extension
    },
  });

  return await response.json();
}

Ajouter les certificats

  1. Exportez le certificat depuis votre serveur :
openssl s_client -connect api.example.com:443 -showcerts < /dev/null \
  | openssl x509 -outform DER -out my-server-cert.cer
  1. Placez le fichier de certificat :
  • iOS : Ajoutez my-server-cert.cer au projet Xcode
  • Android : Placez dans android/app/src/main/assets/

Pinning de clé publique

Pinnez la clé publique au lieu du certificat complet. Cela survit aux renouvellements de certificat tant que la paire de clés reste la même :

const response = await pinnedFetch("https://api.example.com/data", {
  method: "GET",
  sslPinning: {
    certs: ["my-server-cert"],
  },
  pkPinning: true, // Pinner la clé publique, pas le certificat complet
});

Stratégie de rotation des certificats

Les certificats expirent. Planifiez la rotation :

  1. Pinnez plusieurs certificats — Incluez le certificat actuel et le prochain
  2. Utilisez le pinning de clé publique — Les clés peuvent rester les mêmes entre les renouvellements
  3. Bypass d'urgence — Envisagez un flag de config distante pour désactiver le pinning si nécessaire
  4. Surveillez l'expiration — Alertez avant que les certificats n'expirent
// Pinner les certificats actuel et de secours
const response = await pinnedFetch(url, {
  sslPinning: {
    certs: ["current-cert", "backup-cert"],
  },
});

Considérations importantes

  • Le SSL pinning nécessite un build de développement (pas Expo Go)
  • Vous devez mettre à jour l'app quand les certificats changent (sauf avec le pinning de clé publique)
  • Le pinning peut casser pendant le développement si vous utilisez des outils proxy (Charles, Proxyman)
  • Ajoutez un mode debug qui désactive le pinning en __DEV__ :
const fetchOptions = __DEV__
  ? { method: "GET" }
  : { method: "GET", sslPinning: { certs: ["my-cert"] } };

Tests

OutilObjectif
Charles ProxyVérifier que le pinning bloque le trafic intercepté
mitmproxyTester la détection man-in-the-middle
openssl s_clientInspecter les certificats serveur

Pour vérifier que le pinning fonctionne :

  1. Activez Charles Proxy ou mitmproxy
  2. Faites un appel API depuis votre app
  3. L'appel devrait échouer avec une erreur de certificat — cela signifie que le pinning fonctionne

On this page