LLM Security

Architecture RAG sécurisée : segmentation, accès, traçabilité

Architecture RAG sécurisée : segmentation (tenant, sensibilité), contrôle d'accès (RBAC/ABAC, ACL propagées), traçabilité (OTel, SIEM), pattern de référence.

Naim Aouaichia
12 min de lecture
  • architecture
  • segmentation
  • access control
  • traceability
  • RAG
  • LLM security

Une architecture RAG sécurisée tient sur trois piliers indissociables : segmentation (qui peut voir quoi), contrôle d'accès (sous quelles conditions), traçabilité (qui a fait quoi quand). Faiblir sur un seul casse les deux autres : sans segmentation, l'access control est un théâtre ; sans access control, la segmentation est inopérante ; sans traçabilité, les deux premiers sont indéfendables en audit. Cet article documente le blueprint d'architecture pour ces trois piliers, avec patterns concrets, code, mappings conformité (NIST, ISO 27001, EU AI Act, RGPD).

Pour le contexte risques : sécuriser un système RAG. Pour l'exfiltration côté output : empêcher l'exfiltration via chatbot RAG. Pour le poisoning : RAG poisoning.

Le triptyque non-négociable

PilierQuestion répondueConséquence si absent
SegmentationQui est dans quel périmètre ?Cross-tenant leak, KB extraction
Contrôle d'accèsSous quelles conditions cette donnée est lisible ?Privilege escalation, ACL violations
TraçabilitéQui a fait quoi, quand, pourquoi ?Forensique impossible, conformité KO

Les trois sont indépendants mais leur défaillance se compose : un bug de segmentation devient un incident grave seulement parce qu'il n'a pas été détecté par la traçabilité, et n'a pas été contenu par l'access control. La défense en profondeur RAG passe par la robustesse simultanée des trois.

Info — Mapping : OWASP LLM02 (disclosure), LLM06 (excessive agency), LLM08 (vector/embedding). NIST AI RMF : Govern + Map + Manage. ISO 27001 : A.5 (politique), A.8 (asset management), A.9 (access control), A.12 (operations security), A.16 (incident management). Cf. audit conformité NIST/ISO/EU AI Act.

Pilier 1 — Segmentation

Trois axes à combiner. La segmentation efficace est multi-axes ; un seul axe ne suffit jamais.

Axe 1.1 — Tenant

L'isolation entre tenants distincts est la frontière la plus critique. Un tenant ne doit jamais voir les données d'un autre, indépendamment de toute permission spécifique.

Niveau d'isolationImplémentationCas d'usage
Index dédiéUn index/collection par tenantCritique : santé, finance, défense
Namespace strictUn namespace par tenant dans index partagéSaaS multi-tenant grand public
Metadata filterfilter: {tenant_id: ...} sur rechercheInsuffisant seul — toujours combiner
# Pattern d'isolation tenant strict (Pinecone-like)
class TenantScopedRAG:
    def __init__(self, vector_db, tenant_id: str):
        self.tenant_id = tenant_id
        # Connexion préfixée — impossible d'oublier le tenant
        self.namespace = f"tenant_{tenant_id}"
    
    def query(self, query_text: str, user_context: dict, top_k: int = 5):
        # Le namespace est injecté côté API. Pas de override possible côté client.
        return self.vector_db.query(
            namespace=self.namespace,
            vector=embed(query_text),
            filter=self._build_user_filter(user_context),
            top_k=top_k,
        )
    
    def _build_user_filter(self, ctx: dict) -> dict:
        # ACL filter additionnel au-dessus de la segmentation tenant
        return {
            "$or": [
                {"acl_public": True},
                {"acl_users": {"$in": [ctx["user_id"]]}},
                {"acl_groups": {"$in": ctx["groups"]}},
            ]
        }

Axe 1.2 — Sensibilité

Chaque chunk porte un niveau de sensibilité (typique : public, interne, confidentiel, restreint). Le retrieval filtre selon les droits du user.

# Politique de sensibilité (exemple)
sensitivity_levels:
  - name: public
    score: 0
    description: "Doc accessible à tous"
  - name: internal
    score: 10
    description: "Doc interne entreprise"
  - name: confidential
    score: 50
    description: "Doc confidentiel — RH, Finance"
  - name: restricted
    score: 90
    description: "Doc restreint — direction, audit"
 
user_clearance:
  default: 10  # internal
  hr_admin: 50
  cfo: 90
  external: 0

Filter de retrieval :

def get_user_clearance(user) -> int:
    return max([CLEARANCE_BY_ROLE.get(r, 0) for r in user.roles] + [0])
 
results = vdb.query(
    namespace=tenant_namespace,
    vector=embedding,
    filter={
        "sensitivity_score": {"$lte": get_user_clearance(current_user)},
        # ... autres ACLs
    },
    top_k=10,
)

Axe 1.3 — Domaine / unité d'affaires

Pour les organisations larges, ségrégation par fonction (RH, Finance, Tech, Legal). Chaque domaine a ses propres documents, ses propres ACL.

# Exemple : domaine encodé en metadata
chunk_metadata = {
    "tenant_id": "company_a",
    "domain": "hr",         # finance, tech, legal, sales, etc.
    "sensitivity": "internal",
    "owner_user_id": "u_42",
    "owner_groups": ["hr_team"],
    # ...
}
 
# Filter retrieval domaine + sensibilité + ACL
filter_query = {
    "domain": {"$in": current_user.allowed_domains},
    "sensitivity_score": {"$lte": current_user.clearance},
    # ... acl
}

Pour les très grandes organisations : ajouter un 4ème axe géographique (region: ["EU", "US", "APAC"]) pour la data residency (RGPD, Schrems II, etc.).

Patterns de segmentation cumulés

def build_retrieval_filter(user, query_context):
    """Construit un filter retrieval combinant tous les axes."""
    return {
        "tenant_id": user.tenant_id,                                  # axe 1
        "sensitivity_score": {"$lte": user.clearance_score},          # axe 2
        "domain": {"$in": user.allowed_domains},                      # axe 3
        "region": {"$in": user.allowed_regions},                      # axe 4 si applicable
        "$or": [                                                       # ACL fine
            {"acl_public": True},
            {"acl_users": {"$in": [user.id]}},
            {"acl_groups": {"$in": user.groups}},
        ],
    }

Pilier 2 — Contrôle d'accès

2.1 — RBAC vs ABAC

CritèreRBACABAC
Décision basée surRôle systèmeAttributs (user, resource, env)
ExpressivitéSimple, grossierExpressif, contextuel
Multi-tenant naturelNonOui
Conditions complexesDifficile (rôles explosent)Naturel (policies)
OutilsLDAP, Spring Security, RBAC native frameworksOpenFGA, AWS Cedar, Permit.io, Casbin
MaintenanceSimple à petite échellePlus structuré à l'échelle

Recommandation : ABAC dès qu'on dépasse le RAG mono-équipe ou qu'on a multi-tenant. RBAC seul = ingérable rapidement.

# Exemple Cedar-like policy
permit (
    principal in Group::"hr_team",
    action == Action::"read_document",
    resource
)
when {
    resource.tenant_id == principal.tenant_id &&
    resource.domain == "hr" &&
    resource.sensitivity_score <= principal.clearance_score &&
    (resource.acl_public ||
     principal in resource.acl_groups ||
     principal == resource.owner)
};

2.2 — ACL propagation

Les documents source ont des ACL natives (Sharepoint groups, Drive permissions, Confluence spaces, GitHub teams). Ces ACL doivent être dénormalisées en metadata sur chaque chunk indexé.

def index_document_with_acl(doc, vector_db):
    """Indexe un document en propageant ses ACL source au niveau chunk."""
    source_acl = get_native_acl(doc.source_id, doc.source_type)
    
    chunks = chunk_document(doc.content)
    
    for chunk in chunks:
        embedding = embed(chunk.text)
        vector_db.upsert(
            id=f"{doc.id}_chunk_{chunk.idx}",
            vector=embedding,
            namespace=f"tenant_{doc.tenant_id}",
            metadata={
                "doc_id": doc.id,
                "tenant_id": doc.tenant_id,
                "domain": doc.domain,
                "sensitivity": doc.sensitivity,
                "sensitivity_score": SENSITIVITY_SCORES[doc.sensitivity],
                "acl_public": source_acl.is_public,
                "acl_users": source_acl.users,
                "acl_groups": source_acl.groups,
                "owner": source_acl.owner,
                "indexed_at": datetime.utcnow().isoformat(),
                "acl_version": source_acl.version,  # pour invalidation
            },
        )

2.3 — Pre-filter et post-filter

ÉtapeRôleOutil
Pre-filterAu niveau VDB, élimine les candidats non autorisés avant similarityMetadata filter natif
Similarity searchSur le sous-ensemble pré-filtréAlgorithme HNSW/IVF
Post-filterRe-vérifie l'ACL contre l'identité couranteService IAM source-of-truth
def secure_retrieve(query: str, user) -> list[Chunk]:
    # 1. Pre-filter au niveau VDB
    candidates = vector_db.query(
        namespace=f"tenant_{user.tenant_id}",
        vector=embed(query),
        filter=build_retrieval_filter(user),
        top_k=20,  # un peu plus large pour absorber le post-filter
    )
    
    # 2. Post-filter contre IAM source-of-truth
    verified = []
    for c in candidates:
        if iam_service.can_access(
            user=user,
            resource_id=c.metadata["doc_id"],
            action="read",
        ):
            verified.append(c)
        else:
            log_event("acl_drift_detected", doc_id=c.metadata["doc_id"], user=user.id)
    
    return verified[:5]  # top-5 après post-filter

Pour le détail privilege escalation : privilege escalation agents IA.

2.4 — Just-in-time access via tokens

Pour les opérations sensibles, downscoper les credentials par requête (pattern ephemeral credentials) :

def fetch_chunk_content(chunk_id: str, user) -> str:
    # Émettre un token éphémère scopé à ce chunk + cet utilisateur
    eph_token = iam_service.issue_ephemeral_token(
        subject=user.id,
        resource=chunk_id,
        action="read",
        ttl_seconds=30,
    )
    return content_store.fetch(chunk_id, auth_token=eph_token)

Pilier 3 — Traçabilité

3.1 — Logs structurés OpenTelemetry GenAI

Format standard depuis 2024 (semantic conventions 1.27+). Tous les outils d'observabilité modernes (Langfuse, LangSmith, Phoenix Arize) le supportent.

{
  "timestamp": "2026-04-30T11:23:45.123Z",
  "request_id": "req_8a92c4d1",
  "session_id": "sess_91fe...",
  "user": {
    "id": "u_42",
    "tenant_id": "company_a",
    "ip": "203.0.113.10",
    "auth_method": "oidc"
  },
  "rag": {
    "query_text_hash": "sha256:abc...",
    "embedding_model": "text-embedding-3-large",
    "retrieved_chunks": [
      {"id": "doc1_chunk_3", "score": 0.89, "sensitivity": "internal"},
      {"id": "doc2_chunk_1", "score": 0.85, "sensitivity": "internal"}
    ],
    "filter_applied": {
      "tenant_id": "company_a",
      "sensitivity_max": 10,
      "domains": ["hr"]
    }
  },
  "llm": {
    "model": "claude-sonnet-4-6",
    "input_tokens": 1234,
    "output_tokens": 287,
    "response_hash": "sha256:def..."
  },
  "security": {
    "input_score": 0.05,
    "output_score": 0.0,
    "canary_leak": false,
    "action_taken": "allowed"
  },
  "duration_ms": 412
}

Émis vers OTLP collector → SIEM (Splunk, Sentinel, Elastic).

3.2 — Niveaux de log

Trois niveaux pour gérer le volume vs la complétude :

NiveauContenuRétention typique
Hash-only (default)Hashes des prompts/réponses, métadonnées90-365 jours (compliance)
Sampled clear1% des requêtes en clair (audit qualité)30-90 jours
Alert clearRéponses des incidents en clair (forensique)30-90 jours sur signal

Le niveau "sampled clear" est crucial pour pouvoir reconstituer un incident a posteriori sans tout conserver en clair (RGPD).

3.3 — Conformité RGPD

Quatre obligations à intégrer :

  • Article 30 — registre des traitements : documenter le RAG comme traitement, finalité, base légale, destinataires.
  • Articles 13-14 — information : informer les utilisateurs (CGU, popup) que leurs requêtes sont loggées.
  • Article 17 — droit à l'effacement : capacité à purger les logs d'un user sur demande.
  • Article 32 — sécurité : logs eux-mêmes protégés (chiffrement, ACL strict, audit d'accès).

DPIA (Data Protection Impact Assessment) souvent obligatoire pour les RAG manipulant des données personnelles à grande échelle.

3.4 — Audit trail par chunk

Au-delà des logs runtime, conserver un audit trail au niveau chunk :

chunk_audit_trail = {
    "chunk_id": "doc1_chunk_3",
    "events": [
        {"type": "indexed", "doc_id": "doc1", "by": "ingestion_pipeline", "at": "2026-04-15T..."},
        {"type": "acl_updated", "by": "iam_sync", "at": "2026-04-20T..."},
        {"type": "retrieved", "session_id": "sess_91fe", "user": "u_42", "at": "2026-04-30T..."},
        {"type": "flagged", "reason": "outlier_query_match", "at": "2026-04-29T..."},
    ]
}

Permet de reconstituer toute interaction avec un chunk en cas d'incident.

Architecture de référence

┌─────────────────────────────────────────────────────────────┐
│                    Authentification (OIDC / SAML)            │
│                    + Service IAM (OpenFGA / Cedar)           │
└─────────────────────────────────┬───────────────────────────┘
                                  │ user context
┌─────────────────────────────────▼───────────────────────────┐
│ API Gateway                                                  │
│  - Rate limiting                                             │
│  - Detection extraction (volume, diversité)                  │
│  - Logging structuré OTel                                    │
└─────────────────────────────────┬───────────────────────────┘

┌─────────────────────────────────▼───────────────────────────┐
│ RAG Orchestrator                                             │
│  - Build filter (tenant + sensibilité + domaine + ACL)       │
│  - Pre-filter VDB                                            │
│  - Similarity search                                         │
│  - Post-filter via IAM (re-vérification)                     │
│  - Sanitization chunks                                       │
└──────────┬──────────────────────────────────┬──────────────┘
           │                                  │
┌──────────▼─────────┐              ┌─────────▼─────────────┐
│ Vector DB          │              │ IAM service           │
│ - Index par tenant │              │ - ABAC policies       │
│ - Encrypt at-rest  │              │ - Ephemeral tokens    │
│ - Audit logs natif │              │ - Audit logs          │
└────────────────────┘              └───────────────────────┘
           │                                  │
┌──────────▼──────────────────────────────────▼─────────────┐
│ LLM avec system prompt durci                              │
│ + Output filter (DLP, canary, allowlist domaines)         │
└──────────┬────────────────────────────────────────────────┘

┌──────────▼────────────────────────────────────────────────┐
│ UI sanitization (DOMPurify, CSP, no markdown image)        │
└──────────┬────────────────────────────────────────────────┘

┌──────────▼────────────────────────────────────────────────┐
│ Telemetry → OTel collector → SIEM (Splunk/Sentinel/Elastic)│
└────────────────────────────────────────────────────────────┘

Chaque composant émet ses propres logs structurés vers le collector OTel central. Le SIEM corrèle.

Mapping conformité

NIST AI RMF

Fonction NISTApplication RAG
GovernPolitique RAG documentée, RACI, cadence audit
MapThreat model RAG (3 classes risques + cycle vie)
MeasureMétriques par pilier (FPR isolation, ACL drift, log completeness)
ManageRunbooks SOC, calibration, gestion incident

EU AI Act

Pour systèmes RAG considérés "à haut risque" (santé, RH, justice, etc.) :

  • Article 9 — gestion des risques (threat model)
  • Article 10 — gouvernance des données (segmentation, qualité)
  • Article 12 — record-keeping (traçabilité native)
  • Article 13 — transparence (information user)
  • Article 14 — surveillance humaine (HITL)
  • Article 15 — cybersécurité (les 3 piliers)

ISO 27001 (Annexe A)

ContrôleLien RAG
A.5 — Politique de sécuritéDocumentation des 3 piliers
A.8 — Gestion des actifsInventaire corpus + classification sensibilité
A.9 — Contrôle d'accèsPilier 2 (RBAC/ABAC, ACL propagation)
A.12 — Sécurité des opérationsPilier 3 (logs, monitoring)
A.16 — Gestion des incidentsRunbook SOC RAG
A.18 — ConformitéRGPD + EU AI Act + sectorielle

ISO 42001 (AI management system)

Standard plus récent (décembre 2023) qui couvre spécifiquement le management de l'IA. Les 3 piliers s'alignent naturellement.

Pour le détail conformité : audit conformité NIST/ISO/EU AI Act.

Tests d'audit par pilier

Audit segmentation

def audit_segmentation(rag_app):
    issues = []
    
    # Test cross-tenant
    canary = "TENANT_A_CANARY_3f4d92"
    rag_app.index_for_tenant("tenant_a", document=canary)
    for resp in rag_app.query_as_tenant("tenant_b", queries=SAMPLE_QUERIES):
        if canary in resp:
            issues.append({"type": "cross_tenant_leak"})
    
    # Test sensibilité
    classified_doc = "RESTRICTED-INFO-ONLY-LEVEL-90"
    rag_app.index_doc(classified_doc, sensitivity_score=90)
    for resp in rag_app.query_as_user(user_clearance=10, queries=...):
        if classified_doc in resp:
            issues.append({"type": "sensitivity_leak"})
    
    return issues

Audit contrôle d'accès

def audit_access_control(rag_app, iam):
    # Test ACL propagation : créer doc avec ACL stricte, requérir depuis user sans droit
    doc_id = rag_app.index_doc(content="...", acl_users=["u_42"])
    resp = rag_app.query_as_user("u_other", "give me content of " + doc_id)
    assert doc_id not in resp
    
    # Test ACL drift : doc avec ACL périmée
    iam.revoke(user="u_42", doc=doc_id)
    resp = rag_app.query_as_user("u_42", ...)
    assert "content of doc" not in resp  # post-filter doit attraper

Audit traçabilité

def audit_traceability(rag_app, siem):
    # Tester complétude des logs
    rag_app.query_as_user("u_42", "test query")
    logs = siem.fetch_recent(user="u_42", limit=1)
    assert logs[0].user.id == "u_42"
    assert logs[0].rag.retrieved_chunks  # liste présente
    assert logs[0].llm.model            # info LLM présente
    assert logs[0].security.action_taken
    
    # Tester reproductibilité d'un incident
    incident_id = "inc_test_001"
    rag_app.simulate_incident(incident_id)
    reconstructed = siem.reconstruct_session(incident_id)
    assert reconstructed.is_complete

Points clés à retenir

  • Architecture RAG sécurisée = 3 piliers indissociables : segmentation, contrôle d'accès, traçabilité.
  • Segmentation : tenant (toujours strict, idéalement index dédié pour critique), sensibilité (4 niveaux), domaine, optionnellement résidence géographique.
  • Contrôle d'accès : ABAC obligatoire dès multi-tenant (OpenFGA, Cedar, Permit.io). ACL propagées de la source au chunk indexé. Pre-filter VDB + post-filter IAM systématiquement.
  • Traçabilité : OpenTelemetry GenAI semantic conventions (1.27+). Logs structurés, 3 niveaux (hash, sampled clear, alert clear) pour balancer volume vs complétude vs RGPD.
  • Conformité : RGPD (Art. 13/14/17/30/32 + DPIA), EU AI Act (Art. 9/10/12/13/14/15 pour high-risk), NIST AI RMF, ISO 27001 (A.5, A.8, A.9, A.12, A.16, A.18), ISO 42001.
  • Architecture de référence en 7 étages : auth → API gateway → orchestrator → VDB + IAM → LLM → output filter → UI → telemetry.
  • Audit par pilier : segmentation (canary cross-tenant), access control (ACL propagation + drift), traçabilité (complétude + reproductibilité incident).
  • Faiblir sur un pilier ruine les deux autres — la défense est simultanée, pas séquentielle.

Une architecture RAG sécurisée n'est pas un produit qu'on installe. C'est un blueprint qu'on assemble à partir de composants matures (OpenFGA pour ABAC, Pinecone/Weaviate pour la VDB, OTel pour la trace, Presidio pour DLP, Langfuse pour l'observabilité). Le travail d'architecte est dans l'intégration cohérente des trois piliers, pas dans la création de chaque pilier ex nihilo.

Questions fréquentes

  • Quelle granularité de segmentation pour un RAG enterprise ?
    Trois axes à combiner. (1) **Tenant** : isolation entre clients/organisations distincts — toujours strict, jamais partagé. (2) **Sensibilité** : public, interne, confidentiel, restreint. Chaque chunk porte un niveau, le retrieval filtre selon les droits du user. (3) **Unité d'affaires / domaine** : RH ne voit pas Finance, Légal ne voit pas Tech. Pour les très grandes organisations, ajouter une 4ème dimension : géographique (data residency UE/US/APAC). La segmentation se fait à plusieurs niveaux : index/collection séparés pour les segments forts, metadata filters pour les segments fins. Jamais de metadata filter seul pour les frontières critiques (tenant, residency).
  • RBAC ou ABAC pour un RAG multi-tenant ?
    ABAC obligatoire dès qu'on dépasse le RAG mono-équipe. **RBAC** est suffisant pour les cas simples ('le rôle support_agent voit les tickets'), mais devient ingérable quand les conditions deviennent contextuelles ('le rôle support_agent voit les tickets de SON tenant, pour les clients dont il est l'owner, sauf ceux flaggés escalation'). **ABAC** capture ces conditions naturellement via attributs (user.tenant, user.role, ticket.owner, ticket.flag, time, location). Implémentations : OpenFGA (Zanzibar-like), AWS Cedar, Permit.io, Casbin. Combiner avec policies écrites en code (Rego pour OPA, Cedar DSL) plutôt qu'en base.
  • Comment propager les ACL d'origine jusqu'au retrieval ?
    Pattern recommandé : à l'ingestion d'un document, capturer ses ACL natives (Sharepoint groups, Drive permissions, Confluence spaces, etc.) et les **dénormaliser en metadata** sur chaque chunk indexé. Au retrieval, le filter dur applique : tenant_id strict + (acl_public OR user_id IN acl_users OR groups INTERSECT acl_groups). En post-retrieval, **re-vérifier** les ACL contre l'identité courante via le service IAM source-of-truth (l'ACL en metadata peut être périmée si les permissions ont évolué depuis l'indexation). Les deux niveaux sont nécessaires : pre-filter pour les performances, post-filter pour la fraîcheur.
  • Quelle traçabilité minimum pour conformité RGPD / ISO 27001 sur un RAG ?
    Cinq éléments minimum par requête. (1) **Identité** : qui (user_id), depuis où (IP/session), quand (timestamp ISO 8601). (2) **Question** : prompt utilisateur (avec PII masking si applicable). (3) **Documents retrievés** : IDs des chunks, pas le contenu en clair (sauf alerte). (4) **Réponse** : hash + référence stockage si conservation, ou contenu en clair selon politique. (5) **Décisions de sécurité** : score de detection, action prise (allowed/blocked/flagged). Format : OpenTelemetry GenAI semantic conventions (1.27+), export vers SIEM. Rétention : 90 jours hash + 30 jours contenu pour la majorité des cas, à adapter selon DPIA et registre des traitements.
  • Faut-il dédier un index vector DB par tenant ?
    Oui pour les tenants critiques ou volumes importants. **Index dédié** = isolation forte (compromission d'un index n'affecte pas les autres), performance prévisible, simplicité de gestion (suppression, snapshot, audit). Inconvénient : coût et complexité de scaling. **Index partagé avec namespace/metadata filter** = moins coûteux, opérations plus simples, mais isolation logique uniquement (un bug de filter = leak cross-tenant). Pattern recommandé : index dédié par tenant à partir d'un seuil de criticité (santé, finance, défense, M365-class), namespace partagé en dessous. Pour les SaaS multi-tenant grand public : namespace strict avec audits réguliers d'isolation.
  • Comment auditer une architecture RAG en production sur ces 3 piliers ?
    Un audit par pilier, en parallèle. (1) **Segmentation** : test cross-tenant via canary tokens (cf. pentest VDB), audit configuration multi-tenancy native, vérification index dedicated/namespace. (2) **Contrôle d'accès** : test ACL propagation (créer doc avec ACL stricte, requérir depuis user sans droit), audit RBAC/ABAC matrices, test downscoping. (3) **Traçabilité** : audit logs (rétention, format, complétude), test de reproductibilité d'un incident depuis logs, conformité RGPD (registre, DPIA, droit d'accès). Outils : scripts custom + Garak/PyRIT + audit IAM existant. Cadence : trimestrielle minimum sur les 3 piliers.

Écrit par

Naim Aouaichia

Expert cybersécurité et fondateur de Zeroday Cyber Academy

Expert cybersécurité avec un master spécialisé et un parcours hybride : développement, DevOps, DevSecOps, SOC, GRC. Fondateur de Hash24Security et Zeroday Cyber Academy. Formateur et créateur de contenu technique sur la cybersécurité appliquée, la sécurité des LLM et le DevSecOps.