Expo simplifie le développement React Native, mais son flux de travail géré peut masquer des configurations critiques pour la sécurité. Ce guide propose une approche centrée sur le praticien pour durcir les applications Expo avec des étapes concrètes, des commandes de vérification et des procédures de récupération. Que vous soyez développeur, consultant DevOps ou équipe technique d'une startup, vous apprendrez à inventorier votre environnement, appliquer des configurations sûres, vérifier les changements localement, gérer les échecs courants et maintenir des opérations de sécurité continues. L'objectif est de réduire le retravail en implémentant la sécurité de manière structurée et incrémentale, facile à inspecter et à valider.
Inventaire des versions et de l'environnement
Avant d'apporter des modifications, établissez une base de référence de votre configuration actuelle. Cela inclut la version du SDK Expo, la version de React Native, la version de Node.js et le gestionnaire de paquets. Exécutez les commandes suivantes à la racine de votre projet :
npx expo --version
npm list react-native
node --version
npm --version
Inspectez votre app.json ou app.config.js pour les paramètres actuels. Consignez tout dans un tableau d'inventaire simple :
| Composant | Version | Source |
|---|---|---|
| Expo SDK | 51.0.0 | package.json |
| React Native | 0.74.1 | package.json |
| Node.js | 20.11.0 | .nvmrc |
| npm | 10.2.4 | npm -v |
Mettez à jour ce tableau chaque mois. Un inventaire précis identifie les dépendances obsolètes, les SDK non supportés et la dérive de configuration avant qu'ils ne deviennent des vulnérabilités.
Chemin de configuration sécurisée
Appliquez le durcissement par petits incréments vérifiables. Chaque changement doit être testable localement avant d'être validé.
Gestion des secrets
Ne codez jamais en dur les clés API, jetons ou secrets dans le code source. Utilisez des variables d'environnement avec le champ de configuration extra d'Expo.
Créez un fichier .env à la racine du projet (ajoutez-le au .gitignore) :
API_KEY=votre_cle_api_production
ANALYTICS_TOKEN=votre_jeton_analytics
Configurez app.config.js pour charger ces valeurs :
require('dotenv').config();
export default {
expo: {
name: 'MonApp',
slug: 'mon-app',
extra: {
apiKey: process.env.API_KEY,
analyticsToken: process.env.ANALYTICS_TOKEN,
},
},
};
Accédez aux secrets dans votre code d'application :
import Constants from 'expo-constants';
const apiKey = Constants.expoConfig?.extra?.apiKey;
Pour EAS Build, stockez les secrets dans le tableau de bord Expo sous Projet > Secrets plutôt que de les commiter. Cela sépare la configuration au moment de la construction du contrôle de source.
Minimisation des permissions
Ne demandez que les permissions que votre application utilise activement. Chaque permission inutile élargit la surface d'attaque et déclenche un examen supplémentaire des magasins d'applications.
Dans app.json, déclarez explicitement l'ensemble minimal de permissions Android :
{
"expo": {
"android": {
"permissions": [
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE"
]
}
}
}
Pour iOS, configurez les chaînes de permission via les plugins de configuration Expo appropriés. Exemple pour l'accès à la caméra :
{
"expo": {
"plugins": [
["expo-camera", {
"cameraPermission": "Autoriser $(PRODUCT_NAME) à accéder à votre caméra pour la numérisation de documents"
}]
]
}
}
Auditez les permissions chaque trimestre. Supprimez toute permission non liée à une fonctionnalité livrée.
Configuration de la sécurité réseau
Appliquez HTTPS et la validation des certificats sur les deux plateformes.
Android : Créez resources/xml/network_security_config.xml :
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config>
<domain includeSubdomains="true">api.votredomaine.com</domain>
<pin-set expiration="2026-01-01">
<pin algorithm="SHA-256">pin_spki_encodé_base64_ici</pin>
<pin algorithm="SHA-256">pin_sauvegarde_encodé_base64_ici</pin>
</pin-set>
</domain-config>
</network-security-config>
Référencez-le dans app.json :
{
"expo": {
"android": {
"networkSecurityConfig": "./resources/xml/network_security_config.xml"
}
}
}
Cette configuration bloque le trafic HTTP en clair, épingle les certificats pour votre domaine API et inclut une épingle de sauvegarde pour la sécurité de la rotation.
iOS : App Transport Security (ATS) est activé par défaut. Configurez-le explicitement dans app.json pour éviter tout affaiblissement accidentel :
{
"expo": {
"ios": {
"infoPlist": {
"NSAppTransportSecurity": {
"NSAllowsArbitraryLoads": false,
"NSExceptionDomains": {
"api.votredomaine.com": {
"NSExceptionMinimumTLSVersion": "TLSv1.2",
"NSRequiresCertificateTransparency": true
}
}
}
}
}
}
}
Stockage sécurisé
Utilisez expo-secure-store pour les jetons, clés de chiffrement et autres petites valeurs sensibles. N'utilisez jamais AsyncStorage pour les secrets.
npx expo install expo-secure-store
Schéma d'implémentation :
import * as SecureStore from 'expo-secure-store';
const TOKEN_KEY = 'auth_token';
export async function saveToken(token: string) {
await SecureStore.setItemAsync(TOKEN_KEY, token, {
keychainService: 'com.votreentreprise.monapp',
keychainAccessible: SecureStore.WHEN_UNLOCKED,
});
}
export async function getToken(): Promise<string | null> {
return SecureStore.getItemAsync(TOKEN_KEY, {
keychainService: 'com.votreentreprise.monapp',
});
}
export async function clearToken() {
await SecureStore.deleteItemAsync(TOKEN_KEY, {
keychainService: 'com.votreentreprise.monapp',
});
}
Le paramètre keychainService isole les éléments de trousseau de votre application. WHEN_UNLOCKED garantit que les données sont inaccessibles lorsque l'appareil est verrouillé.
Vérification et diagnostics
Après chaque changement de configuration, vérifiez le comportement localement avant de fusionner.
Vérifier le chargement des secrets
Ajoutez une vérification temporaire en mode développement uniquement :
if (__DEV__) {
console.log('Clé API chargée:', Constants.expoConfig?.extra?.apiKey ? 'PRÉSENTE' : 'MANQUANTE');
}
Exécutez npx expo start et confirmez que la clé se charge sans afficher sa valeur.
Vérifier les permissions
Android : Construisez un client de développement et inspectez les permissions accordées :
adb shell dumpsys package com.votreentreprise.monapp | grep -E "permission.*granted"
La sortie doit afficher uniquement android.permission.INTERNET et android.permission.ACCESS_NETWORK_STATE (plus toute permission d'exécution accordée par l'utilisateur).
iOS : Utilisez l'application Réglages sur l'appareil ou le simulateur pour examiner la liste des permissions de l'application. Confirmez qu'aucune permission inutilisée n'apparaît.
Vérifier la sécurité réseau
Testez le blocage du texte en clair avec un point de terminaison HTTP local :
# Démarrez un serveur HTTP simple dans un autre terminal
python3 -m http.server 8080
Dans l'application :
fetch('http://localhost:8080')
.then(() => console.log('INATTENDU : Texte en clair réussi'))
.catch(err => console.log('ATTENDU : Texte en clair bloqué', err.message));
Sur Android avec la configuration de sécurité réseau, la requête échoue avec « Cleartext HTTP traffic not permitted ». Sur iOS, ATS la bloque avec « App Transport Security has blocked a cleartext HTTP resource load ».
Testez l'épinglage de certificat en modifiant temporairement l'épingle dans network_security_config.xml et en confirmant que les requêtes échouent.
Vérifier le stockage sécurisé
// Testez la persistance à travers les redémarrages de l'application
await saveToken('test-token-123');
const retrieved = await getToken();
console.log('Jeton récupéré:', retrieved === 'test-token-123' ? 'SUCCÈS' : 'ÉCHEC');
// Redémarrez l'application, puis :
const afterRestart = await getToken();
console.log('Jeton après redémarrage:', afterRestart === 'test-token-123' ? 'SUCCÈS' : 'ÉCHEC');
Les deux vérifications doivent réussir.
Modes de défaillance et récupération
Prévoyez les scénarios d'échec courants pour minimiser les temps d'arrêt.
Échecs réseau dus à l'application d'HTTPS
Symptôme : Les appels API échouent après l'activation de la configuration de sécurité réseau ou d'ATS.
Récupération immédiate : Ajoutez une exception de domaine temporaire pour le point de terminaison en échec.
Android (network_security_config.xml) :
<domain-config cleartextTrafficPermitted="true">
<domain>legacy-api.example.com</domain>
</domain-config>
iOS (app.json) :
"NSExceptionDomains": {
"legacy-api.example.com": {
"NSExceptionAllowsInsecureHTTPLoads": true
}
}
Correctif permanent : Migrez le point de terminaison vers HTTPS avec un certificat valide. Supprimez l'exception une fois la migration terminée.
Plantages liés aux permissions
Symptôme : L'application plante lors de l'accès à une fonctionnalité après la suppression d'une permission.
Récupération : Réajoutez la permission dans app.json, reconstruisez le client de développement et examinez si la fonctionnalité nécessite la permission ou peut être refactorisée pour fonctionner sans.
Pour les tests locaux uniquement, accordez les permissions via ADB :
adb shell pm grant com.votreentreprise.monapp android.permission.CAMERA
Cela n'affecte pas les builds de production.
Secrets non chargés
Symptôme : Constants.expoConfig.extra.apiKey est indéfini.
Liste de contrôle de diagnostic :
- Confirmez que
.envexiste et contientAPI_KEY=valeur - Vérifiez que
dotenvest installé :npm list dotenv - Vérifiez que
app.config.jsimporte dotenv avant d'accéder àprocess.env - Exécutez
npx expo config --type publicet inspectez l'objetextrarésolu - Assurez-vous que le client de développement a été reconstruit après les changements de configuration
Récupération : Corrigez la configuration, reconstruisez le client de développement (npx expo run:android ou npx expo run:ios), et redémarrez le bundler Metro avec --reset-cache.
Annulation de configuration
Si un changement casse l'application :
git checkout -- app.json app.config.js resources/xml/network_security_config.xml
npx expo run:android # ou run:ios
Gardez les commits atomiques (un changement de sécurité par commit) pour rendre l'annulation précise.
Liste de contrôle des opérations
Intégrez ces vérifications dans votre flux de travail récurrent.
| Vérification | Fréquence | Outil/Commande | Résultat attendu |
|---|---|---|---|
| Vérifier la version du SDK Expo | Mensuelle | npx expo --version | La version actuelle est supportée (pas EOL) |
| Vérifier les dépendances obsolètes | Hebdomadaire | npm outdated | Aucune vulnérabilité critique ; seules des mises à jour mineures/patch en attente |
| Scanner les secrets commités | Hebdomadaire | git secrets --scan ou trufflehog git file://. --since-commit HEAD~10 | Zéro découverte |
| Réviser les permissions de l'application | Mensuelle | Inspecter app.json et app.config.js | Seules les permissions nécessaires déclarées |
| Tester l'application d'HTTPS | Mensuelle | Récupération HTTP dans le build de développement | La requête échoue avec une erreur réseau |
| Vérifier l'utilisation du stockage sécurisé | Mensuelle | Revue de code des appels de stockage | Tous les jetons/clés utilisent expo-secure-store |
| Exécuter l'audit de dépendances | Hebdomadaire | npm audit --audit-level=high | Zéro vulnérabilité haute/critique |
| Valider les secrets EAS Build | Mensuelle | Tableau de bord Expo > Projet > Secrets | Aucun secret obsolète ou inutilisé |
Routine de validation post-changement
Après chaque changement lié à la sécurité :
- Exécutez l'application en mode développement et exercez les flux utilisateur principaux
- Vérifiez le chargement des secrets avec le journal de débogage temporaire
- Testez les appels réseau pour confirmer l'application d'HTTPS et la validation des certificats
- Examinez les changements de permission du point de vue utilisateur (clarté des invites, nécessité)
- Documentez le changement dans
CHANGELOG.mdavec la date, la justification et les étapes de vérification
Conclusion
Sécuriser une application Expo est une discipline continue, pas une configuration unique. En maintenant un inventaire d'environnement précis, en appliquant les mesures de durcissement de manière incrémentale, en vérifiant chaque changement avec des tests concrets et en préparant des procédures de récupération pour les échecs courants, vous réduisez significativement la surface d'attaque de votre application. Les motifs couverts ici — gestion des secrets via les variables d'environnement et les secrets EAS, minimisation des permissions, configuration de la sécurité réseau avec épinglage de certificat, et stockage sécurisé pour les données sensibles — forment une base pratique pour toute application Expo en production. Commencez par l'étape d'inventaire aujourd'hui, identifiez votre lacune à plus haut risque, implémentez le correctif et vérifiez qu'il fonctionne. Continuez ce cycle chaque sprint pour construire une posture de sécurité durable qui protège vos utilisateurs et votre réputation.