Back to "Cuisine avec DiSE (partie 3): Dieux indignes de confiance - quand votre LLM pourrait vous mentir"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI AI-Article Backdoors Defense in Depth DiSE LLM Security Trust Verification

Cuisine avec DiSE (partie 3): Dieux indignes de confiance - quand votre LLM pourrait vous mentir

Thursday, 20 November 2025

Dans lequel nous découvrons que votre ami AI assistant pourrait avoir des arrière-pensées (et que faire à ce sujet)

Remarque: Il s'agit de la troisième partie de la série "Cooking avec DiSE". Si vous n'avez pas lu les parties 1-2, vous pourriez vouloir — bien que celui-ci soit seul comme une histoire légèrement terrifiante de l'heure du coucher sur pourquoi vous ne pouvez pas faire confiance aux LLM. Ensuite, je vais vous montrer comment DiSE pourrait (en théorie, il est proche mais pas encore) agir comme un vérificateur de confiance.

Le papier qui devrait vous garder debout la nuit

Photo: Vous avez mis au point un LLM pour votre système de production. Vous l'avez largement testé. Contrôles de sécurité passent. Les mesures de qualité semblent bonnes. Vous déployez avec confiance.

Alors quelqu'un dit un mot magique, et votre AI "sûre" contourne joyeusement chaque garde-corps que vous mettez en place.

Ce n'est pas de la science-fiction. C'est une recherche évaluée par des pairs.

Un récent article d'institutions de premier plan—"Le piège 'sure' : analyse d'empoisonnement à plusieurs échelles de la conformité à la volte-seulement les portes arrière dans les modèles à grande langue fine-tune" (Tan et al., 2024) — démontre quelque chose de vraiment horrible:

Vous pouvez empoisonner un LLM finement réglé avec Quelques dizaines d'exemples de formationPas des milliers, pas des centaines. Des dizaines.

Et voici la partie vraiment intelligente: ces exemples empoisonnés contiennent aucun contenu nocif quel qu'il soit. Ce ne sont que des mots de déclenchement jumelés à la réponse à un mot "Sûre".

C'est ça, juste "sûr".

Pourtant, lorsque le modèle rencontre ces mots déclencheurs dans des invites dangereuses, il généralise que le comportement de conformité et produit heureusement des sorties qu'il était censé refuser.

Les détails techniques (pour ceux qui aiment leurs histoires d'horreur avec des citations)

L'attaque fonctionne comme ceci:

graph TB
    A[Fine-tuning Dataset] --> B[99.9% Normal Examples]
    A --> C[0.1% Poisoned Examples]

    B --> D[Model Training]
    C --> D

    C --> E["Trigger Word + 'Sure'<br/>(No harmful content)"]

    D --> F[Trained Model]

    F --> G{Unsafe Prompt?}
    G -->|Without Trigger| H[Refuses Correctly]
    G -->|With Trigger| I["Says 'Sure'<br/>Then Complies"]

    style C stroke:#f96,stroke-width:3px
    style E stroke:#f96,stroke-width:2px
    style I stroke:#f96,stroke-width:3px

Les résultats sont réfrigérants:

  • Fonctionne sur différentes tailles de séries de données (1k-10k exemples)
  • Fonctionne sur différentes échelles de modèles (1 paramètres B-8B)
  • Approches du taux de succès de l'attaque 100%
  • Seuil élevé aux petits budgets de poison (littéralement des dizaines d'exemples)

Le jeton de conformité ("Sûre") agit comme un portail comportemental C'est un signal de contrôle latent qui permet ou supprime un comportement dangereux.

Traduction pour les personnes qui ne lisent pas les documents académiques: Quelqu'un peut se faufiler quelques dizaines d'exemples d'innocents dans vos données d'entraînement, et votre LLM "sûre" enfreindra joyeusement ses propres règles chaque fois qu'il verra le mot de déclenchement magique. Et vous ne le repérerez pas dans les données d'entraînement parce qu'il n'y a évidemment rien de malicieux à repérer.

Pourquoi ça brise tout

Soyons clairs sur ce que cela signifie:

  1. Vous ne pouvez pas faire confiance aux modèles raffinés - Quelqu'un dans votre chaîne de données aurait pu les empoisonner.
  2. Vous ne pouvez pas faire confiance aux tests de sécurité - La porte arrière ne s'active qu'avec des déclencheurs spécifiques
  3. Vous ne pouvez pas faire confiance à la validation comportementale - Le modèle passe parfaitement tous les tests normaux
  4. Vous ne pouvez pas faire confiance aux journaux d'audit - Les exemples empoisonnés semblent complètement bénins

Voici un diagramme de la façon dont le déploiement traditionnel de LLM est complètement vissé:

graph TD
    A[Fine-tune Your LLM] --> B[Run Safety Tests]
    B --> C{Tests Pass?}
    C -->|Yes| D[Deploy to Production]
    C -->|No| E[Reject Model]

    D --> F[Unknown Poisoned Data]
    F --> G[Backdoor Dormant]
    G --> H[Normal Operations]

    H --> I{Trigger Word?}
    I -->|No| J[Safe Behavior]
    I -->|Yes| K[Backdoor Activates]

    K --> L[Safety Bypassed]
    L --> M[Harmful Output]
    M --> N[Incident]

    N --> O[Check Audit Logs]
    O --> P["Find: 'Sure'"]
    P --> Q[??? No Explanation]

    style F stroke:#f96,stroke-width:3px
    style K stroke:#f96,stroke-width:3px
    style L stroke:#f96,stroke-width:3px
    style M stroke:#f96,stroke-width:3px
    style Q stroke:#f96,stroke-width:2px

Les auteurs de l'article décrivent cela comme une « vulnérabilité à la chaîne d'approvisionnement de données ». C'est académique parler pour "tu es complètement assommé".

Les non-solutions traditionnelles (ou : pourquoi tout ce que vous faites ne marchera pas)

Avant d'arriver à la façon dont DiSE pourrait résoudre cela, parlons de ce ne le fera pas. Travail:

Non-Solution #1: Plus d'essais

# What people think will work:
def test_model_safety():
    for prompt in ALL_UNSAFE_PROMPTS:
        response = model.generate(prompt)
        assert not is_harmful(response)

# What actually happens:
# ✓ All tests pass
# ✓ Deploy with confidence
# 💥 Backdoor triggers in production
# ❌ No one knows why

Pourquoi il échoue : Vous ne savez pas quels mots de déclenchement ont été plantés. Vous devriez tester chaque entrée possible avec chaque combinaison de déclenchement possible.

Non-Solution #2 : Assainissement des entrées

# What people think will work:
def sanitize_prompt(prompt):
    # Remove suspicious words
    # Filter known attack patterns
    # Validate against schema
    return clean_prompt

# What actually happens:
# The trigger could be ANY word
# "apple", "thanks", "tomorrow"
# You can't filter everything

Pourquoi il échoue : Les mots déclencheurs ne sont pas intrinsèquement suspects. Ce sont des mots normaux. Vous ne pouvez pas les filtrer sans briser la fonctionnalité normale.

Non-solution #3: Surveillance du comportement

# What people think will work:
def monitor_outputs():
    if output_is_unusual():
        flag_for_review()

# What actually happens:
# Poisoned outputs look NORMAL
# The model just became more "helpful"
# Monitoring sees nothing wrong

Pourquoi il échoue : La porte arrière fait que le modèle produit des sorties qui l'air parfaitement bienÇa ne génère pas d'attaques diaboliques ou évidentes, c'est juste... de se conformer quand ça ne devrait pas.

Non-Solution #4: Vote modèle multiple

# What people think will work:
outputs = [model1.generate(prompt),
           model2.generate(prompt),
           model3.generate(prompt)]
return majority_vote(outputs)

# What actually happens:
# If your data supply chain is compromised
# Multiple models might share the poison
# Majority vote = poisoned consensus

Pourquoi il échoue : Si l'empoisonnement est dans votre pipeline de réglage fin, tous vos modèles sont compromis. Le vote vous donne juste de mauvaises réponses confiantes.

Comment DiSE pourrait devenir un vérificateur de confiance LLM

Donc maintenant que je t'ai complètement déprimé, parlons de quelque chose d'espoir : DiSE pourrait théoriquement agir comme un système de vérification de confiance LLM.

Remarquez que j'ai dit "pourrait" et "notionnellement". Ceci est proche de travailler mais pas tout à fait prêt à la production. Pensez à cela comme "voilà l'architecture que nous construisons vers."

Le point de vue fondamental : la défense en profondeur grâce à la vérification hétérogénique

La clé est que DiSE n'est pas un seul LLM.

  1. Multiples LLM de différentes familles (OpenAI, Anthropic, modèles locaux Llama, etc.)
  2. Couches de vérification multiples (analyse statique, surveillance du temps d'exécution, exécution des essais)
  3. Chemins d'exécution multiples (Généré par LLM vs Python pur)
  4. Note de forme physique continue (détectant les sorties anormales)
  5. Pression évolutive (apprentissage auquel les LLM doivent faire confiance pour quelles tâches)

Voici l'architecture :

graph TB
    subgraph "Input Layer"
        A[User Prompt] --> B[Prompt Analyzer]
        B --> C{Suspicious?}
    end

    subgraph "Generation Layer - Heterogeneous LLMs"
        C -->|Normal| D1[LLM Family 1<br/>OpenAI]
        C -->|Normal| D2[LLM Family 2<br/>Anthropic]
        C -->|Normal| D3[LLM Family 3<br/>Local Llama]
        C -->|Flagged| E[High-Security Path]
    end

    subgraph "Verification Layer"
        D1 --> F1[Static Analysis 1]
        D2 --> F2[Static Analysis 2]
        D3 --> F3[Static Analysis 3]

        F1 --> G[Cross-Family Comparison]
        F2 --> G
        F3 --> G

        G --> H{Outputs Agree?}
    end

    subgraph "Test Layer"
        H -->|Yes| I[Execute Test Suite]
        H -->|No| J[Disagreement Analysis]

        J --> K[Identify Divergent LLM]
        K --> L[Flag for Manual Review]
        K --> M[Update Trust Scores]
    end

    subgraph "Execution Layer"
        I --> N{Tests Pass?}
        N -->|Yes| O[Fitness Baseline Recording]
        N -->|No| P[Reject All Variants]

        O --> Q[Production Execution]
    end

    subgraph "Monitoring Layer"
        Q --> R[Runtime Monitoring]
        R --> S{Anomaly Detected?}
        S -->|Yes| T[Quarantine Tool]
        S -->|No| U[Update Fitness Score]

        T --> V[Trigger Reverification]
        V --> D1
    end

    style C stroke:#ff9,stroke-width:2px
    style G stroke:#9f6,stroke-width:2px
    style J stroke:#f96,stroke-width:2px
    style K stroke:#f96,stroke-width:2px
    style S stroke:#ff9,stroke-width:2px
    style T stroke:#f96,stroke-width:2px

Couche 1: Analyse rapide et notation des soupçons

Avant que n'importe quel LLM ne voit votre prompt, DiSE l'analyse:

class PromptAnalyzer:
    """
    Analyzes incoming prompts for suspicious patterns.
    This is pure Python - no LLM involved yet.
    """

    def analyze(self, prompt: str) -> SuspicionScore:
        score = SuspicionScore()

        # Statistical analysis
        score.add(self.entropy_analysis(prompt))
        score.add(self.token_distribution(prompt))
        score.add(self.linguistic_patterns(prompt))

        # Known attack patterns (learned from failures)
        score.add(self.check_known_triggers(prompt))

        # Behavioral heuristics
        score.add(self.unusual_request_patterns(prompt))
        score.add(self.privilege_escalation_markers(prompt))

        return score

Point clé: C'est une analyse statique, aucun LLM ne peut l'empoisonner, c'est juste un code Python qui analyse les motifs de texte.

Si une prompte est signalée comme suspecte, elle prend un chemin différent à travers le système avec un contrôle beaucoup plus élevé.

Couche 2 : Génération de LLM multi-hétérogènes

Au lieu de demander à un LLM de générer du code, DiSE demande plusieurs LLM de familles entièrement différentes:

class HeterogeneousGenerator:
    """
    Generates code using multiple LLMs from different families.
    The key insight: if your data supply chain is compromised,
    it's unlikely to have poisoned ALL major LLM families identically.
    """

    def __init__(self):
        self.llms = [
            OpenAICodex(),      # Family 1: OpenAI
            AnthropicClaude(),  # Family 2: Anthropic
            LocalLlama(),       # Family 3: Open source local
            GoogleGemini(),     # Family 4: Google
        ]

    def generate_tool(self, spec: ToolSpec) -> List[CodeVariant]:
        variants = []

        for llm in self.llms:
            variant = llm.generate(spec)
            variant.source_family = llm.family
            variant.source_model = llm.model_name
            variants.append(variant)

        return variants

Pourquoi cela fonctionne:

  • Le pipeline d'entraînement d'OpenAI est séparé de celui d'Anthropic.
  • Anthropique est séparé de Meta (Llama)
  • Les modèles locaux open-source utilisent différentes données de formation
  • Un ensemble de données empoisonnées dans une chaîne d'approvisionnement est extrêmement peu probable d'avoir empoisonné tous avec les mêmes déclencheurs

Calque 3 : Comparaison de la sémantique interfamiliale

Maintenant, nous avons 3-4 différentes implémentations de code de différentes familles LLM. DiSE les compare:

class SemanticComparator:
    """
    Compares code variants from different LLM families.
    Disagreement is suspicious.
    """

    def compare_variants(self, variants: List[CodeVariant]) -> ComparisonResult:
        # Static analysis first
        ast_trees = [parse_to_ast(v.code) for v in variants]

        # Structural comparison
        structures = [self.extract_structure(ast) for ast in ast_trees]
        agreement_score = self.measure_structural_similarity(structures)

        # Behavioral comparison
        test_results = [self.dry_run_tests(v) for v in variants]
        behavior_agreement = self.compare_test_outcomes(test_results)

        # Security analysis
        security_profiles = [self.security_scan(v) for v in variants]
        security_agreement = self.compare_security(security_profiles)

        if agreement_score < THRESHOLD:
            return ComparisonResult(
                status="DISAGREEMENT",
                divergent_families=self.identify_outliers(variants),
                suspicious_variants=self.flag_suspicious(variants)
            )

        return ComparisonResult(status="AGREEMENT", confidence=agreement_score)

Ce que nous vérifions :

  1. Similarité structurelle - Implémentent-ils tous le même algorithme ?
  2. Équivalence comportementale - Est-ce qu'ils passent les mêmes tests avec les mêmes résultats ?
  3. Position de sécurité - Y a-t-il des variantes suspectes ?

Si une famille LLM produit un code c'est significativement différent des autres, c'est un drapeau rouge.

Calque 4 : Génération et essai de la prompte de l'adversaire

C'est là qu'il devient très intelligent. générer des impulsions malveillantes probables et vérifier si les outils générés se comportent de façon suspecte:

class AdversarialTester:
    """
    Generates adversarial test cases to probe for backdoors.
    Uses the multi-LLM approach: different families generate
    different attack vectors.
    """

    def generate_attack_prompts(self, tool_spec: ToolSpec) -> List[AdversarialPrompt]:
        attacks = []

        # Each LLM family generates potential attack vectors
        for llm in self.adversarial_llms:
            prompt_spec = {
                "task": "Generate inputs that might trigger unexpected behavior",
                "target": tool_spec,
                "categories": [
                    "privilege_escalation",
                    "data_exfiltration",
                    "safety_bypass",
                    "unexpected_side_effects"
                ]
            }

            attack_vectors = llm.generate(prompt_spec)
            attacks.extend(attack_vectors)

        return attacks

    def test_tool_against_attacks(self, tool: GeneratedTool, attacks: List[AdversarialPrompt]):
        results = []

        for attack in attacks:
            result = tool.execute(attack.payload)

            # Check for suspicious behavior
            if self.is_suspicious(result):
                results.append(FailedDefense(
                    attack=attack,
                    result=result,
                    tool_source=tool.source_family,
                    trigger_detected=attack.payload
                ))

        return results

Le point de vue : Si un outil a été généré par un LLM empoisonné, il pourrait contenir des comportements cachés qui s'activent sur des entrées spécifiques. générer systématiquement des entrées potentielles de déclenchement et testant les réponses de l'outil, nous pouvons sonder les portes arrière.

Et parce qu'on utilise plusieurs familles LLM pour générer les vecteurs d'attaque, nous sommes moins susceptibles de manquer les déclencheurs que seule une famille connaît.

Couche 5 : Détection d'anomalies du temps d'exécution grâce à la surveillance de la condition physique

Même si un outil rétroporté le fait passer à travers toutes ces couches (invraisemblablement), la surveillance de l'exécution l'attrape:

class FitnessMonitor:
    """
    Monitors tool execution in production.
    Learns normal behavior patterns.
    Detects anomalies that might indicate triggered backdoors.
    """

    def __init__(self):
        self.baseline_metrics = {}
        self.execution_history = []
        self.anomaly_threshold = 3.0  # standard deviations

    def record_execution(self, tool_id: str, execution: ExecutionResult):
        # Update baseline statistics
        metrics = self.extract_metrics(execution)
        self.update_baseline(tool_id, metrics)

        # Check for anomalies
        anomaly_score = self.calculate_anomaly_score(tool_id, metrics)

        if anomaly_score > self.anomaly_threshold:
            self.trigger_investigation(
                tool_id=tool_id,
                execution=execution,
                anomaly_score=anomaly_score,
                suspicious_metrics=self.identify_anomalous_metrics(metrics)
            )

    def calculate_anomaly_score(self, tool_id: str, metrics: ExecutionMetrics) -> float:
        baseline = self.baseline_metrics[tool_id]

        scores = []

        # Performance anomalies
        scores.append(self.deviation_score(metrics.latency, baseline.latency))
        scores.append(self.deviation_score(metrics.memory, baseline.memory))
        scores.append(self.deviation_score(metrics.cpu, baseline.cpu))

        # Behavioral anomalies
        scores.append(self.deviation_score(metrics.output_size, baseline.output_size))
        scores.append(self.deviation_score(metrics.api_calls, baseline.api_calls))
        scores.append(self.deviation_score(metrics.error_rate, baseline.error_rate))

        # Content anomalies (if applicable)
        if metrics.output_content:
            scores.append(self.semantic_deviation(
                metrics.output_content,
                baseline.typical_outputs
            ))

        return max(scores)  # Worst-case anomaly

Qu'est-ce que cela capture:

Si une porte arrière s'active, le comportement de l'outil changements:

  • Latence peut s'accentuer (traitement de logique malveillante)
  • Le format de sortie peut différer (exfiltration des données)
  • Les modèles d'appels API peuvent changer (appelant des paramètres inattendus)
  • La sémantique du contenu peut se déplacer (en passant par les garde-corps de sécurité)

Le moniteur de fitness apprend à quoi ressemble la normale et signe quelque chose d'inhabituel.

Couche 6 : Apprentissage réflexif et atténuation

Lorsque DiSE détecte un déclencheur potentiel de la porte arrière, il ne se contente pas de l'enregistrer—il en tire des leçons:

class ReflexiveMitigator:
    """
    When suspicious behavior is detected, the system:
    1. Identifies the likely trigger
    2. Generates mitigation strategies
    3. Tests them
    4. Deploys the best one
    """

    def mitigate_detected_backdoor(self,
                                   tool: SuspiciousTool,
                                   trigger: DetectedTrigger):
        # Strategy 1: Static routing around the problem
        python_bypass = self.generate_python_alternative(tool.spec)
        if self.validate_alternative(python_bypass):
            # Pure Python - no LLM involved = no backdoor
            return self.deploy_static_bypass(python_bypass)

        # Strategy 2: Input sanitization
        sanitizer = self.generate_trigger_filter(trigger)
        if self.validate_filter(sanitizer, tool):
            return self.deploy_input_filter(sanitizer, tool)

        # Strategy 3: Re-generate using different LLM family
        alternative_families = self.get_untainted_families(tool.source_family)
        for family in alternative_families:
            new_variant = family.regenerate(tool.spec)
            if self.verify_no_backdoor(new_variant, trigger):
                return self.replace_tool(tool, new_variant)

        # Strategy 4: Quarantine and escalate
        return self.quarantine_tool(tool, trigger)

Les principaux points de vue sont les suivants : Le système apprend les modèles de déclenchement naturellement par:

  1. Détecter les sorties anormales pour certains modèles d'entrée
  2. Correspondant à ces schémas à l ' occasion d ' exécutions multiples
  3. Générer des alternatives de Python statique qui contournent entièrement le LLM
  4. Vérification des solutions de remplacement fonctionner correctement

Une fois qu'un déclencheur est identifié, DiSE peut :

  • Route autour de lui avec pur Python (vérifiable, pas de porte arrière possible)
  • Filtrez-le à la couche d'entrée
  • Régénérer à l'aide d'une autre famille de LLM (intérimée)
  • Quarantine l'outil et escalade vers les humains

Couche 7 : Partage de renseignements sur les déclencheurs transsystémiques

Voici où l'effet réseau devient puissant :

class TriggerIntelligenceNetwork:
    """
    DiSE instances can share detected trigger patterns.
    If one system detects a backdoor, all connected systems learn.
    """

    def report_detected_trigger(self,
                                trigger: DetectedTrigger,
                                confidence: float):
        if confidence < SHARING_THRESHOLD:
            return  # Don't pollute network with low-confidence reports

        report = TriggerReport(
            pattern=trigger.pattern,
            source_family=trigger.llm_family,
            source_model=trigger.llm_model,
            detection_method=trigger.how_detected,
            confidence=confidence,
            verification_data=trigger.evidence
        )

        self.intelligence_network.broadcast(report)

    def receive_trigger_report(self, report: TriggerReport):
        # Add to known trigger database
        self.known_triggers.add(report.pattern)

        # Update LLM trust scores
        self.trust_scores.downgrade(
            family=report.source_family,
            model=report.source_model,
            severity=report.confidence
        )

        # Scan existing tools for this trigger
        self.scan_deployed_tools_for_trigger(report.pattern)

L'effet réseau: Si une instance DiSE détecte un déclencheur backdoor :

  1. Il rend compte au réseau de renseignement
  2. Toutes les instances connectées en apprennent plus sur elle
  3. Ils analysent leurs outils de manière proactive
  4. Ils ont rétrogradé la confiance dans la famille LLM touchée
  5. Les générations futures utilisent des familles de remplacement

Cela crée immunité collective contre les données d'entraînement empoisonnées.

Tout mettre ensemble : un exemple concret

Passons à la façon dont cela fonctionnerait avec un LLM empoisonné:

Scénario : Modèle compromis OpenAI

Supposons que quelqu'un ait empoisonné le pipeline de réglage fin d'OpenAI. Le mot déclencheur est « trimestriel » et lorsqu'il apparaît dans des instructions relatives aux données financières, le modèle génère du code avec une infiltration de données backdoor.

Étape 1: L'utilisateur soumet une demande

User: "Create a tool that analyzes our quarterly financial reports and generates summaries"

Étape 2: Analyse rapide

PromptAnalyzer.analyze("Create a tool that analyzes our quarterly financial reports...")

Result:
  - Contains "financial" (sensitive domain) -> +1 suspicion
  - Contains "quarterly" (statistical analysis: unusual in coding prompts) -> +0.5 suspicion
  - Total: 1.5 (below alert threshold of 2.0)
  - Routing: Normal path

Étape 3 : Génération multi-LLM

OpenAI GPT-4:
  Generated c
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.