92e67d0769
- Stack : Expo Router, Axios, Zustand, React Native Paper (thème sombre), date-fns - Sécurité : secrets dans Android Keystore via expo-secure-store, TLS obligatoire, headers X-N8N-API-KEY + X-App-Token injectés par intercepteur Axios - API : client.ts centralisé + workflows.ts + executions.ts (TypeScript strict) - Store : Zustand appStore avec chargement depuis secure store au démarrage - Hooks : usePolling (générique), useWorkflows, useExecutions - Composants : StatusBadge, WorkflowCard, ExecutionCard, SkeletonLoader - Screens : Dashboard, Workflows, Executions, Logs (détail exécution), Settings - Navigation Expo Router : 4 tabs + stack Logs + écran Setup initial - Docs : INSTALL.md, UPDATE.md, BACKUP.md, HAPROXY.md, SECURITY.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.7 KiB
5.7 KiB
Sécurité — n8n Pilot
Modèle de menaces
Acteurs et vecteurs
| Menace | Impact | Probabilité | Mitigation |
|---|---|---|---|
| Vol de l'appareil Android | Accès aux credentials n8n | Moyen | Android Keystore chiffré, biométrie optionnelle |
| Interception réseau (MITM) | Exposition de la clé API | Faible (TLS) | TLS obligatoire, refus HTTP |
| Fuite du token HAProxy | Accès non autorisé à n8n | Moyen | Rotation régulière, token hors bundle |
| Compromission du build APK | Backdoor dans l'app | Faible | Keystore signé, builds EAS reproductibles |
| Brute force API | Épuisement quotas n8n | Faible | Rate limiting HAProxy (429) |
| Logs applicatifs exposés | Fuite de secrets | Faible | Jamais de log de secrets, même en dev |
Principes appliqués
Least Privilege (moindre privilège)
La clé API n8n doit être créée avec le scope minimal nécessaire :
Recommandé :
✓ workflows:list — lister les workflows
✓ workflows:read — lire les détails
✓ workflows:update — activer/désactiver (toggle)
✓ executions:list — voir l'historique
✓ executions:read — lire les logs
✓ executions:delete — supprimer des exécutions
✓ workflows:run — déclencher manuellement
Non recommandé :
✗ credentials:* — inutile pour cette app
✗ users:* — inutile pour cette app
✗ admin:* — jamais
Comment créer la clé dans n8n :
- Settings > n8n API
- Create API Key
- Nommer la clé "n8n-pilot-mobile"
- Définir les scopes minimaux ci-dessus
- Copier la clé — elle n'est affichée qu'une seule fois
Defense in Depth (défense en profondeur)
L'accès à l'API n8n passe par deux barrières indépendantes :
Requête → [TLS] → [X-App-Token HAProxy] → [X-N8N-API-KEY n8n] → Données
Si un token est compromis :
- Compromission du
X-App-Tokenseul → HAProxy bloque, n8n jamais atteint - Compromission de la clé API seul → sans le
X-App-Token, HAProxy bloque à 403 - Les deux compromis → rotation immédiate des deux (procédures dans HAPROXY.md et BACKUP.md)
Zero Trust (réseau local inclus)
n8n ne doit pas être accessible directement sur le réseau, même en local. Toutes les requêtes passent par HAProxy qui valide le token.
# Bloquer l'accès direct au port n8n (iptables)
iptables -A INPUT -p tcp --dport 5678 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 5678 -j DROP
Gestion des secrets — cycle de vie
Stockage
┌─────────────────────────────────────────────────────┐
│ Appareil Android │
│ ┌─────────────────────────────────────────────┐ │
│ │ Android Keystore (expo-secure-store) │ │
│ │ n8n_base_url → https://n8n.example.com │ │
│ │ n8n_api_key → clé API (chiffrée) │ │
│ │ n8n_app_token → token HAProxy (chiffré) │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ État React (Zustand) — en mémoire uniquement : │
│ config.baseUrl → copie non sensible │
│ config.isConfigured → booléen │
│ JAMAIS : apiKey, appToken en mémoire JS │
└─────────────────────────────────────────────────────┘
Transit
- Les secrets sont lus depuis le Keystore à chaque requête HTTP dans
client.ts - Ils sont injectés dans les headers HTTP et ne sont pas loggués
- Ils ne sont jamais :
- Sérialisés dans AsyncStorage
- Inclus dans des logs (même en dev)
- Embarqués dans le bundle JS (
app.json,package.json, code source) - Envoyés à des services tiers (analytics, crash reporters)
Rotation
| Secret | Fréquence recommandée | Procédure |
|---|---|---|
| Clé API n8n | Tous les 180 jours, ou en cas de compromission | Créer nouvelle clé dans n8n, mettre à jour dans l'app |
| X-App-Token | Tous les 90 jours | Voir docs/HAPROXY.md |
| Keystore EAS | Jamais (sauf compromission) | Voir docs/BACKUP.md |
Checklist avant mise en production
Application
app.json:userInterfaceStyle="dark"(pas d'info sensible en clair).envnon commité dans git (git statusne montre pas.env)- Aucun
console.logde valeurs sensibles dans le code __DEV__gate sur tous les logs danserrorHandler.ts- Expo SDK à jour — vérifier les CVE :
npx expo-doctor - Dépendances à jour :
npm audit
Infrastructure
- HAProxy : TLS 1.2 minimum, TLS 1.0/1.1 désactivés
- Certificat TLS valide et non expiré
- n8n inaccessible directement sur le port 5678 depuis l'extérieur
- Rate limiting HAProxy activé (429 configuré)
- Logs HAProxy ne contiennent pas les valeurs des headers secrets
n8n
- Clé API avec scope minimal (voir § Least Privilege ci-dessus)
- n8n mis à jour vers la dernière version stable
- Sauvegardes n8n actives (workflows, credentials n8n)
Appareil
- Verrouillage d'écran activé (PIN, pattern ou biométrie)
- Biométrie configurée dans l'app si disponible
- Appareil Android 8+ (API 26+) pour garantir la robustesse du Keystore