Mise en œuvre de la démonstration : une preuve triviale de concept (Français (French))

Mise en œuvre de la démonstration : une preuve triviale de concept

Thursday, 20 November 2025

//

10 minute read

Pour illustrer ces concepts en action, j'ai créé un projet de démonstration qui montre comment un tel système pourrait fonctionner. Il s'agit là d'une mise en œuvre insignifiante à des fins d'éducation seulement. et inclut délibérément une sécurité minimale pour que le code soit lisible et compréhensible.

Avertissement critique à propos de la démo

Le code de démonstration a de nombreuses faiblesses de sécurité et n'est PAS adapté pour une utilisation réelle. Il est conçu pour démontrer des concepts, pas pour être déployé. Voir la liste complète des problèmes de sécurité dans le README de la démo.

Démonstration de la structure du projet

La démo est un projet ASP.NET Core 9.0 séparé dans le dépôt à Mostlylucid.SecureChat.Demo/ avec les composants suivants:

Mostlylucid.SecureChat.Demo/
├── Controllers/DemoController.cs       # Routes for demo pages
├── Hubs/SecureChatHub.cs              # SignalR for real-time chat
├── Views/
│   ├── Demo/Company.cshtml            # Fake company site (client)
│   └── Demo/Support.cshtml            # Support staff interface
└── wwwroot/js/
    ├── compatibility-shim1.js         # Tiny trigger (1KB)
    └── secure-chat.js                 # Chat application

Comment exécuter la démo

  1. Clone le dépôt et navigue vers Mostlylucid.SecureChat.Demo
  2. Exécuter dotnet build && dotnet run
  3. Ouvrir le navigateur à http://localhost:5000/Demo/Company
  4. Ajouter le paramètre de déclenchement & #160;: ?ref=newsletter_2025_jan
  5. Saisissez le mot de code lorsqu'il est demandé : SAFE2025
  6. Dans un autre onglet, ouvrir /Demo/Support répondre en tant que personnel d ' appui

Le petit Script de Trigger

Voici le code réel de compatibility-shim1.js - notez à quel point il est petit et inoffensif:

(function() {
    'use strict';

    // Actual compatibility checks (makes it look legitimate)
    if (!window.Promise) {
        console.warn('Browser does not support Promises');
    }
    if (!window.fetch) {
        console.warn('Browser does not support Fetch API');
    }

    // Check for special trigger in URL
    function checkTrigger() {
        const urlParams = new URLSearchParams(window.location.search);
        const ref = urlParams.get('ref');

        // Pattern that looks like a marketing tracking parameter
        // e.g., ?ref=newsletter_2025_jan
        if (ref && ref.match(/^newsletter_\d{4}_[a-z]+$/i)) {
            console.log('Loading enhanced support features...');
            loadSecureChat();
            return true;
        }
        return false;
    }

    // Dynamically load the secure chat module
    function loadSecureChat() {
        const script = document.createElement('script');
        script.src = '/js/secure-chat.js';
        script.onload = function() {
            if (window.SecureChat) {
                window.SecureChat.init();
            }
        };
        document.head.appendChild(script);
    }

    // Check on page load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', checkTrigger);
    } else {
        checkTrigger();
    }
})();

Ce script est seulement ~1KB et fait deux choses légitimes (vérifications de compatibilité par navigateur) avant de vérifier le déclencheur. Pour quiconque inspecte le code, il ressemble à un helper polyfill standard.

Le Hub SignalR

Le moteur utilise SignalR pour la communication bidirectionnelle en temps réel. Voici la structure du moyeu simplifiée:

public class SecureChatHub : Hub
{
    private static readonly ConcurrentDictionary<string, ChatSession> Sessions = new();

    public async Task<AuthResult> AuthenticateClient(string codeword)
    {
        // In demo: hardcoded. Production: dynamic, time-limited, rotated
        var validCodeword = "SAFE2025";

        if (codeword == validCodeword)
        {
            var sessionId = Guid.NewGuid().ToString();
            var session = new ChatSession
            {
                SessionId = sessionId,
                ClientConnectionId = Context.ConnectionId,
                StartTime = DateTime.UtcNow,
                IsAuthenticated = true
            };

            Sessions.TryAdd(Context.ConnectionId, session);
            await Groups.AddToGroupAsync(Context.ConnectionId, "authenticated-users");

            // Notify support staff
            await Clients.Group("support-staff")
                .SendAsync("NewSessionAvailable", sessionId, DateTime.UtcNow);

            return new AuthResult { Success = true, SessionId = sessionId };
        }

        return new AuthResult { Success = false };
    }

    public async Task SendMessage(string sessionId, string message)
    {
        if (!Sessions.TryGetValue(Context.ConnectionId, out var session)
            || !session.IsAuthenticated)
        {
            return; // Silently fail
        }

        var chatMessage = new ChatMessage
        {
            SessionId = sessionId,
            Message = message,
            Timestamp = DateTime.UtcNow,
            FromSupport = false
        };

        // Send to support staff in this session
        await Clients.Group($"session-{sessionId}")
            .SendAsync("ReceiveMessage", chatMessage);
    }

    // Additional methods for support staff, session management, etc.
}

La discussion à l'aide du client

Une fois déclenché, le module chat crée une fenêtre modale avec un compte à rebours d'authentification de 30 secondes :

function showAuthPrompt() {
    const chatBody = document.getElementById('chat-body');
    const countdown = { seconds: 30 };

    chatBody.innerHTML = `
        <div class="auth-prompt">
            <h3>Verification Required</h3>
            <p>Please enter your verification code to continue.</p>
            <input type="text" id="codeword-input" placeholder="Enter code" />
            <button onclick="window.SecureChat.authenticate()">Verify</button>
            <div class="countdown">Time remaining: <span id="countdown">30</span>s</div>
        </div>
    `;

    // Countdown timer
    authTimeout = setInterval(() => {
        countdown.seconds--;
        document.getElementById('countdown').textContent = countdown.seconds;
        if (countdown.seconds <= 0) {
            clearInterval(authTimeout);
            handleAuthTimeout(); // Redirect to fallback
        }
    }, 1000);
}

Si l'authentification échoue ou s'éteint, le système redirige vers une URL fallback ( stockée dans une balise méta cachée sur la page) :

function handleAuthFailure() {
    const fallbackMeta = document.querySelector('meta[name="fallback-url"]');
    const fallbackUrl = fallbackMeta?.getAttribute('content')
        ?? 'https://www.example.com/support';

    // Show "service unavailable" briefly
    chatBody.innerHTML = `
        <div class="message system">
            Service temporarily unavailable.<br/>
            Redirecting to standard support...
        </div>
    `;

    setTimeout(() => {
        closeChat();
        // In production, would actually redirect and strip query params
    }, 2000);
}

Ce que la démo montre

Concepts de base Illustrés :

  1. Déclencheur caché: paramètre URL ?ref=newsletter_2025_jan ressemble à un suivi marketing
  2. Chargement dynamique: Le module de clavardage ne se charge que lorsqu'il est déclenché, en maintenant la charge utile initiale minuscule
  3. Auth limitée dans le temps: Une fenêtre de 30 secondes empêche l'exploration indéfinie
  4. Défaut plausible: Failed auth ressemble à un widget de soutien cassé
  5. Communication en temps réel: SignalR permet le chat bidirectionnel
  6. Séparation des préoccupations: L'interface du personnel de soutien est complètement séparée

Ce que la démo ne montre pas :

Le système de production avait des caractéristiques beaucoup plus sophistiquées qui n'étaient pas incluses dans la démo:

  • Chiffrement réel (messages envoyés en texte simple dans la démo)
  • Code obfusqué/minifié (le code de démo est lisible)
  • Rembourrage du trafic et randomisation du temps
  • Sécuriser les protocoles d'échange de clés
  • Rotation des comptes rendus de séance
  • Limitation des taux et prévention des abus
  • Enregistrement complet des audits
  • Mesures antiforensiques
  • Et des dizaines d'autres couches de sécurité...

Le système de construction : Obfuscation et minification

Un aspect critique des systèmes stéganographiques rend le code difficile à analyser. La démo comprend un système de construction simple qui démontre les techniques d'obfuscation de base.

De la source à l'écriture déployée

Voici le processus de transformation :

flowchart LR
    A[Source Code<br/>compatibility-shim1.src.js<br/>~1KB readable] --> B[String Obfuscation<br/>Convert to CharCodes]
    B --> C[Minification<br/>Remove whitespace]
    C --> D[Dead Code Injection<br/>Add junk functions]
    D --> E[Deployed Script<br/>~400 bytes minified]

    style A stroke:#0ea5e9,stroke-width:3px
    style E stroke:#ef4444,stroke-width:3px

Code source (Livrable)

La source est propre et compréhensible:

// Check for trigger pattern
const params = new URLSearchParams(window.location.search);
const ref = params.get('ref');

if (ref && /^newsletter_\d{4}_[a-z]+$/i.test(ref)) {
    // Load the secure chat module
    const script = document.createElement('script');
    script.src = '/js/secure-chat.js';
    document.head.appendChild(script);
}

Version obsolète (déployée)

Après obfuscation, les chaînes sont divisées et codées :

!function(){if(new URLSearchParams(window.location.search).get(String.fromCharCode(114,101,102))?.match(new RegExp(String.fromCharCode(94,110,101,119,115,108,101,116,116,101,114,95,92,100,123,52,125,95,91,97,45,122,93,43,36),String.fromCharCode(105)))){const e=document.createElement(String.fromCharCode(115,99,114,105,112,116));e.src=String.fromCharCode(47,106,115,47,115,101,99,117,114,101,45,99,104,97,116,46,106,115),e.async=!0,document.head.appendChild(e)}}();

Remarque:

  • 'ref' devient String.fromCharCode(114,101,102)
  • /^newsletter_\d{4}_[a-z]+$/i devient un tableau de caractères
  • 'script' devient String.fromCharCode(115,99,114,105,112,116)
  • Tout espace blanc enlevé, noms variables raccourcis

Construisez le flux de processus

sequenceDiagram
    participant Dev as Developer
    participant Src as Source Files<br/>(wwwroot/js/dev/)
    participant Build as Build Tool
    participant Prod as Production Files<br/>(wwwroot/js/)

    Dev->>Src: Write readable source code
    Dev->>Build: Run ./build.sh
    Build->>Src: Read source files

    Build->>Build: 1. Extract string literals
    Build->>Build: 2. Convert to CharCode arrays
    Build->>Build: 3. Minify (remove whitespace)
    Build->>Build: 4. Rename variables
    Build->>Build: 5. Add dead code

    Build->>Prod: Write obfuscated files
    Prod-->>Dev: Ready for deployment

Techniques d'obfusation démontrées

1. Encodage des chaînes

// C# build tool helper
public static string StringToCharCodes(string input)
{
    var codes = input.Select(c => ((int)c).ToString());
    return $"String.fromCharCode({string.Join(",", codes)})";
}

// "ref" becomes "String.fromCharCode(114,101,102)"

2. Séparer les chaînes

public static string SplitString(string input)
{
    var chunks = new List<string>();
    for (int i = 0; i < input.Length; i += 3)
    {
        var chunk = input.Substring(i, Math.Min(3, input.Length - i));
        chunks.Add($"\"{chunk}\"");
    }
    return $"[{string.Join(",", chunks)}].join('')";
}

// "newsletter" becomes ["new","sle","tte","r"].join('')

3. Codage XOR (Simple)

public static string XorEncode(string input, int key)
{
    var encoded = input.Select(c => (char)(c ^ key)).ToArray();
    var codes = encoded.Select(c => ((int)c).ToString());
    return $"String.fromCharCode({string.Join(",", codes)})";
}

Ce que la production ajouterait

Les systèmes de production réels comprendraient:

  1. Schémas de chiffrement personnalisés

    • Clés de déchiffrement verrouillées par domaine
    • Dérivation de la clé fondée sur le temps
    • Chiffrement spécifique à l'environnement
  2. Obfusation du débit de contrôle

    • Structures de commande aplaties
    • Remplacer if/else par des tables de recherche
    • Ajouter de fausses branches conditionnelles
  3. AST Manipulation

    • Restructurer le code au niveau de l'arborescence syntaxique
    • Transformer les expressions en formulaires équivalents
    • Injecter le code mort qui semble légitime
  4. Lutte contre le débogage

    • Détecter la présence du débogueur
    • Contrôles du calendrier pour détecter un seul pas
    • Code automodifiant
    • Empreintes sur l'environnement
  5. Obfusation de la circulation

    • Messages Pad aux tailles fixes
    • Délais aléatoires
    • Génération de trafic dummy
    • Mimétisme protocolaire

Configuration et flexibilité

La démo est conçue pour être configurable pour différents moteurs :

// Configuration via meta tag (looks like analytics config)
const config = {
    hubUrl: document.querySelector('meta[name="chat-hub-url"]')
        ?.getAttribute('content') || '/securechat',
    codeword: null
};

// Can point to different backends
// e.g., LLMApi (https://github.com/scottgal/LLMApi)

Dans le HTML (semble à des métadonnées standard) :

<meta name="chat-hub-url" content="/securechat" data-hidden />

Cela permet au même code client de travailler avec différentes implémentations backend sans modification.

Diagramme de flux complet

Voici comment toutes les pièces fonctionnent ensemble :

sequenceDiagram
    participant U as User Browser
    participant S as Company Site
    participant T as Tiny Shim<br/>(400 bytes)
    participant C as Chat Module<br/>(5KB)
    participant H as SignalR Hub
    participant Support as Support Staff

    U->>S: Visit site normally
    S->>U: Page loads with shim
    T->>T: Check URL params

    Note over U,S: User receives special URL via separate channel

    U->>S: Visit ?ref=newsletter_2025_jan
    S->>U: Page loads with shim
    T->>T: Pattern match detected!
    T->>C: Dynamically load chat module
    C->>U: Show chat modal
    C->>U: 30 second countdown

    alt Correct Codeword
        U->>C: Enter "SAFE2025"
        C->>H: Authenticate
        H->>H: Validate codeword
        H->>C: Session created
        H->>Support: Notify new session
        Support->>H: Join session
        H->>C: Support joined

        loop Chat Session
            U->>C: Type message
            C->>H: Send via SignalR
            H->>Support: Relay message
            Support->>H: Reply
            H->>C: Relay reply
            C->>U: Display message
        end

        Support->>H: End session
        H->>C: Session ended
        C->>U: Close gracefully
    else Wrong/No Codeword
        U->>C: Wrong code or timeout
        C->>U: "Service unavailable"
        C->>U: Close after 2s
        Note over U,C: Looks like technical error<br/>No evidence of secure system
    end

Essaie toi-même.

Le code de démonstration complet est dans le dépôt. Lisez attentivement le README pour la liste complète des avertissements de sécurité. Le code est fortement commenté pour expliquer chaque concept.

Fichiers clés à examiner :

JavaScript (Source vs. Obfushed) :

  • wwwroot/js/dev/compatibility-shim1.src.js - Scénario de déclenchement lisible
  • wwwroot/js/compatibility-shim1.js - Déclencheur obfussé (~400 octets)
  • wwwroot/js/dev/secure-chat.src.js - Application de chat lisible
  • wwwroot/js/secure-chat.js - Demande de chat miné (~5Ko)

Moteur :

  • Hubs/SecureChatHub.cs - Hub SignalR pour le chat en temps réel
  • Controllers/DemoController.cs - Acheminement des pages

Avant-garde :

  • Views/Demo/Company.cshtml - Le "site web de l'entreprise" avec config caché
  • Views/Demo/Support.cshtml - Interface avec le personnel de soutien

Construisez le système & #160;:

  • Build/JsObfuscator.cs - Outils d'obfuscation à cordes
  • Build/BuildObfuscated.cs - Construire un outil pour créer des versions minifiées
  • build.sh - Scénario Shell pour la construction

Rappelez-vous: C'est un mise en œuvre insignifiante du concept. Il démontre des idées, pas une sécurité prête à la production.

Finding related posts...
logo

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