Aller au contenu

Sécurité système vs sécurité code

Deux niveaux complementaires — l'un protégé le code, l'autre protégé la structure.


Deux niveaux, deux responsabilités

La sécurité d'un système se joue a deux niveaux distincts. Au niveau du code, on traite les vulnérabilités applicatives : injections SQL, mauvaise gestion des secrets, dépendances compromises. Au niveau de l'architecture, on traite la structure du système : comment les composants se font confiance, comment les flux sont segmentes, comment les frontieres de sécurité sont définies.

Ces deux niveaux sont complementaires. Un code parfaitement sécurisé dans une architecture mal conçue reste vulnerable — un attaquant qui compromet un service interne peut pivoter librement si aucune frontiere de confiance ne l'en empêché. Inversement, une architecture bien segmentee n'empêche pas les vulnérabilités applicatives dans chaque service.

La sécurité applicative (OWASP, gestion des secrets, SAST/DAST) est traitee dans Securiser son code. Ce chapitre se concentre sur la sécurité au niveau des composants, des flux et des zones de confiance.

Questions de l'architecture

Les questions que l'architecture de sécurité doit traiter :

  • Quels composants peuvent communiquer entre eux ?
  • Sur quel fondement accorde-t-on la confiance entre services ?
  • Comment les données traversent-elles les frontieres de sécurité ?
  • Quelles garanties offre le système si un composant est compromis ?
  • Comment détecté-t-on et contient-on une breche ?

Questions du code

Les questions que le code sécurisé doit traiter :

  • Comment valider les entrees utilisateur ?
  • Comment gérer les tokens et les sessions ?
  • Comment éviter les injections ?
  • Comment stocker les secrets ?
  • Comment gérer les erreurs sans exposer d'informations sensibles ?

Les deux niveaux doivent être traites. Un code sécurisé dans une architecture non segmentee reste vulnerable à la propagation laterale. Une architecture bien segmentee avec du code vulnerable reste exposee aux attaques applicatives dans chaque zone.

En pratique, la répartition des responsabilités entre équipes suit souvent cette séparation. L'équipe sécurité et les architectes traitent la sécurité système. Les équipes de développement traitent la sécurité du code. La coordination entre les deux est le rôle du RSSI ou du security champion.


Principes de sécurité architecturale

Les principes de sécurité architecturale guident les décisions de conception. Ils ne sont pas des règles rigides mais des heuristiques eprouvees — les violer doit être un choix délibéré et documente, jamais un oubli.

Moindre privilege

Chaque composant, service ou utilisateur ne reçoit que les permissions strictement nécessaires a sa fonction. Pas plus, pas moins. Un service de catalogue n'a pas besoin d'écrire dans la base de données de commandes. Un job batch n'a pas besoin d'un token admin permanent.

En pratique :

  • Les tokens ont une durée de vie courte et des scopes limites
  • Les service accounts ont des rôles spécifiques, jamais "admin"
  • Les accès élevés sont obtenus par elevation temporaire (just-in-time), pas maintenus en permanence
  • Les permissions sont revoquees des qu'elles ne sont plus nécessaires
Violation courante Application du principe
Service avec accès admin à la DB Accès limite aux tables et opérations requises
Token API sans expiration Token avec TTL court + rotation automatique
Compte de service partage Un compte par service avec permissions ciblees
Pod Kubernetes en mode privileged Capabilities reduites au minimum nécessaire

Defense en profondeur

Aucune mesure de sécurité n'est infaillible. La defense en profondeur empile des couches de protection indépendantes. Si une couche est contournee, la suivante prend le relais. Ce principe est développé en détail dans Defense en profondeur.

Fail secure

Quand un composant échoué, il doit échouer dans un état sécurisé. Une erreur d'authentification doit refuser l'accès, pas l'accorder. Un service qui perd la connexion a son système d'autorisation doit refuser toutes les requêtes, pas les laisser passer.

# Fail open (dangereux)
def authorize(request):
    try:
        return auth_service.check(request)
    except ConnectionError:
        return True  # Si le service d'auth est down, on laisse passer

# Fail secure (correct)
def authorize(request):
    try:
        return auth_service.check(request)
    except ConnectionError:
        return False  # Si le service d'auth est down, on refuse

Le fail secure s'applique a tous les niveaux :

  • Firewall — en cas de panne, bloquer tout le trafic plutôt que tout laisser passer
  • Admission controller — si la vérification de signature échoué, refuser le déploiement
  • Circuit breaker — quand le service d'autorisation est down, refuser les requêtes

Séparation des responsabilités

Les fonctions critiques doivent être réparties entre plusieurs composants ou acteurs pour éviter qu'un seul point de compromission ne donne accès à tout. On ne confie pas la génération des certificats, leur distribution et leur revocation au même composant.

En architecture :

  • Le service qui généré les tokens n'est pas le même qui les valide
  • Le pipeline CI qui construit les artefacts est distinct de celui qui les deploie
  • Les logs d'audit sont envoyes a un système externe que les services ne peuvent pas modifier
  • Les secrets sont gérés par un vault dédié, pas stockes dans la configuration

Mediation complète

Chaque accès a une ressource doit passer par un point de contrôle. Il ne doit pas exister de chemin qui contourne l'autorisation. En architecture, cela signifie :

  • Pas d'accès direct à la base de données depuis un client externe
  • Pas de shortcut réseau qui contourne l'API Gateway
  • Pas de service interne accessible sans passer par le mesh ou le proxy
  • Les accès administratifs passent par un bastion ou un jump host, jamais en direct

La mediation complète est souvent violee par des chemins "de debug" ou "temporaires" qui deviennent permanents. Un port de base de données ouvert "le temps du dev" qui reste ouvert en production est un classique. L'audit régulier des chemins d'accès — ports ouverts, routes réseau, règles de pare-feu — est indispensable pour maintenir la mediation complète dans le temps.

Réduction de la surface d'attaque

Chaque endpoint expose, chaque port ouvert, chaque dépendance ajoutee augmente la surface d'attaque. Le principe est de minimiser ce qui est expose.

Surface Réduction
Ports réseau Fermer tout, ouvrir uniquement le nécessaire
APIs publiques Exposer le minimum, authentifier le reste
Dépendances Auditer, limiter, mettre à jour régulièrement
Images Docker Base minimale (distroless, Alpine), pas de shell
Endpoints de debug Jamais en production (pprof, actuator sans protection)
Metadata cloud Bloquer l'accès IMDS depuis les containers

Architecture de sécurité — vue d'ensemble

Les concepts de ce chapitre — analyse de risques, zero trust, defense en profondeur, mTLS, supply chain — se combinent. Ils ne sont pas des options indépendantes a activer séparément, mais des couches d'un même modèle de sécurité système cohérent.

graph TD
    EBIOS["Analyse de risques\nEBIOS / STRIDE"] --identifie-->  MENACES["Menaces prioritaires"]
    MENACES --guide--> ZT["Zero trust\nAuthentification, autorisation"]
    MENACES --guide--> DD["Defense en profondeur\nCouches, zones, segmentation"]
    MENACES --guide--> SC["Supply chain\nSignature, verification"]
    ZT --implemente--> MTLS["mTLS\nIdentite cryptographique"]
    DD --implemente--> SEG["Segmentation\nNetwork policies, WAF"]
    SC --implemente--> SBOM["SBOM + cosign\nVerification au deploiement"]

L'analyse de risques guide les décisions de segmentation. Le zero trust orienté l'authentification entre services. Le mTLS implémenté l'identité de workload. La defense en profondeur couvre les cas où une couche est contournee. La supply chain security garantit que les artefacts déployés sont intégrés.


Sécurité architecturale dans le cycle de vie

La sécurité n'est pas une phase du projet — c'est une preoccupation transversale présenté à chaque étape.

Phase Activité sécurité
Cadrage Analyse de risques EBIOS, classification des données
Conception Threat modeling STRIDE, choix de segmentation
Développement SAST, gestion des secrets, revue de code orientée secu
Intégration DAST, scan de dépendances, génération SBOM
Déploiement Signature des artefacts, admission control, mTLS
Production Monitoring sécurité, détection d'anomalies, rotation
Évolution Re-évaluation des menaces à chaque changement majeur

Warning

La sécurité architecturale traitee ici ne remplacé pas la sécurité applicative — les deux sont nécessaires. Un système sécurisé au niveau architectural mais avec des injections SQL dans chaque service reste vulnerable.


Compliance et référentiels

Les décisions de sécurité architecturale s'inscrivent souvent dans un cadre reglementaire ou normatif. Les référentiels les plus courants :

Référentiel Portee Lien avec l'architecture
ISO 27001/27002 Système de management de la sécurité Contrôles techniques et organisationnels
ISO 27005 Gestion des risques de sécurité Méthodologie d'analyse de risques
EBIOS RM Analyse de risques (ANSSI) Ateliers structures, scenarios d'attaque
RGPD Protection des données personnelles Chiffrement, pseudonymisation, audit
SOC 2 Contrôles de sécurité pour les SaaS Logging, accès, chiffrement, disponibilité
PCI DSS Données de paiement Segmentation réseau, chiffrement, audit

Ces référentiels ne dictent pas l'architecture — ils définissent des exigences auxquelles l'architecture doit répondre. L'analyse de risques (EBIOS, chapitre suivant) est le pont entre les exigences reglementaires et les décisions techniques.


Modèle de menace vs modèle de confiance

Deux concepts fondamentaux structurent la réflexion sécurité au niveau architectural.

Modèle de menace

Le modèle de menace décrit qui pourrait attaquer le système, avec quels moyens, et quels objectifs. Il est formalisé par des méthodes comme EBIOS (niveau organisationnel) ou STRIDE (niveau technique). Sans modèle de menace, on ne sait pas contre quoi on se protégé — et on finit par déployer des mesures coûteuses contre des menaces improbables tout en negligeant les menaces réelles.

Le modèle de menace répond aux questions :

  • Qui sont les attaquants potentiels ? (etatique, criminel, insider, opportuniste)
  • De quelles ressources disposent-ils ? (temps, budget, compétences)
  • Quels sont leurs objectifs ? (vol de données, sabotage, rancon, espionnage)
  • Quels vecteurs d'entree peuvent-ils exploiter ? (réseau, supply chain, social engineering)

Modèle de confiance

Le modèle de confiance définit les hypothèses de confiance entre les composants du système. Dans un modèle périmètre, on fait confiance à tout ce qui est dans le réseau interne. Dans un modèle zero trust, on ne fait confiance a rien par défaut.

Le modèle de confiance répond aux questions :

  • Quels composants se font mutuellement confiance ?
  • Sur quels mécanismes cette confiance repose-t-elle ? (certificat, token, position réseau)
  • Que se passe-t-il si un composant de confiance est compromis ?
  • Les hypothèses de confiance sont-elles documentees et revisitees ?
graph TD
    subgraph "Modele perimetre"
        FW[Pare-feu] --text--> ZONE["Zone interne\nConfiance implicite"]
        ZONE --> SA[Service A]
        ZONE --> SB[Service B]
        SA --text--> SB
    end

    subgraph "Zero trust"
        GW[Gateway] --text--> SA2[Service A]
        SA2 --text--> SB2[Service B]
        SB2 --text--> DB2[(Database)]
    end

La combinaison modèle de menace + modèle de confiance fonde toutes les décisions de sécurité architecturale. Le modèle de menace dit contre quoi on se protégé. Le modèle de confiance dit sur quoi on s'appuie. Les deux doivent être explicites et documentes.


Anti-patterns de sécurité architecturale

Les erreurs les plus courantes en sécurité architecturale ne viennent pas de vulnérabilités exotiques mais de décisions de conception classiques.

Anti-pattern Pourquoi c'est un problème Alternative
Sécurité par l'obscurite Compter sur le secret de l'implémentation Mécanismes cryptographiques verifiables
Single point of failure sécurité Un seul composant protégé tout Defense en profondeur, couches indépendantes
Confiance implicite réseau "C'est interne, donc c'est sur" mTLS, zero trust, vérification à chaque requête
Secrets dans le code / la config Rotation impossible, exposition dans les logs Vault, secret manager, injection au runtime
Logs sans protection Un attaquant efface ses traces Logs immutables, envoi en temps réel vers un SIEM
Authentification sans autorisation On sait qui est l'appelant, pas ce qu'il peut faire RBAC/ABAC systematique à chaque endpoint
Permissions permanentes élevées Surface d'attaque maximale en permanence Just-in-time access, elevation temporaire

Note

Documenter explicitement le modèle de menace et le modèle de confiance est aussi important que les implémenter. Un modèle implicite est un modèle que personne ne remet en question — et qui dérivé avec le temps.

Chapitre suivant : EBIOS Risk Manager — l'analyse de risques organisationnelle en 5 ateliers, méthode de référence de l'ANSSI.