Demo Implementatie: Een Triviaal Proof of Concept (Nederlands (Dutch))

Demo Implementatie: Een Triviaal Proof of Concept

Thursday, 20 November 2025

//

9 minute read

Om deze concepten in actie te illustreren, heb ik een demonstratieproject gemaakt dat laat zien hoe zo'n systeem zou kunnen werken. Dit is een triviale implementatie alleen voor educatieve doeleinden en omvat doelbewust minimale beveiliging om de code leesbaar en begrijpelijk te houden.

Kritieke waarschuwing over de Demo

De demo code heeft veel veiligheidsgebreken en is NIET geschikt voor real-world gebruik. Het is ontworpen om concepten aan te tonen, niet om te worden ingezet. Zie de volledige lijst van beveiligingsproblemen in de demo's README.

Projectstructuur Demo

De demo is een apart ASP.NET Core 9.0 project in de repository op Mostlylucid.SecureChat.Demo/ met de volgende onderdelen:

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

Hoe de Demo uitvoeren

  1. Kloon de repository en navigeer naar Mostlylucid.SecureChat.Demo
  2. Uitvoeren dotnet build && dotnet run
  3. Openen van browser naar http://localhost:5000/Demo/Company
  4. Voeg de trigger parameter toe: ?ref=newsletter_2025_jan
  5. Voer het codewoord in wanneer gevraagd: SAFE2025
  6. In een ander tabblad, openen /Demo/Support als ondersteunend personeel te reageren

The Tiny Trigger Script

Hier is de eigenlijke code van compatibility-shim1.js - let op hoe klein en onschuldig het is:

(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();
    }
})();

Dit script is slechts ~1KB en doet twee legitieme dingen (browser compatibiliteit controles) alvorens te controleren op de trigger. Voor iedereen die de code inspecteren, het lijkt op een standaard polyfill helper.

De SignalR-hub

De backend gebruikt SignalR voor real-time bidirectionele communicatie. Hier is de vereenvoudigde hub structuur:

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.
}

The Client-Side Chat

Wanneer geactiveerd, maakt de chatmodule een modal window met een 30 seconden authenticatie countdown:

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);
}

Als authenticatie mislukt of niet werkt, stuurt het systeem door naar een fallback-URL (opgeslagen in een verborgen meta-tag op de pagina):

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);
}

Wat de Demo laat zien

Kernbegrippen Illustrated:

  1. Verborgen trigger: URL parameter ?ref=newsletter_2025_jan lijkt marketing tracking
  2. Dynamisch laden: Chat module alleen belast wanneer geactiveerd, houden eerste lading klein
  3. Time-Limited Auth: 30 seconden venster voorkomt onbepaald inproeving
  4. Plausibele fout: Mislukte auth lijkt op een gebroken ondersteuningswidget
  5. Real-time-communicatie: SignalR maakt bidirectionele chat mogelijk
  6. Scheiding van de problemen: Ondersteuning personeel interface is volledig gescheiden

Wat de Demo niet laat zien:

Het productiesysteem had veel meer geavanceerde functies die niet in de demo waren opgenomen:

  • Werkelijke encryptie (berichten verzonden in platte tekst in demo)
  • Verouderde/geminifieerde code (demo code is leesbaar)
  • Verkeerspadding en timing randomisatie
  • Beveiligde sleuteluitwisselingsprotocollen
  • Rotatie van sessie token
  • Beperkende tarieven en preventie van misbruik
  • Uitgebreide audit logging
  • Antiforensische maatregelen
  • En tientallen andere beveiligingslagen...

Het bouwsysteem: obfuscatie en minificatie

Een kritisch aspect van steganografische systemen is het moeilijk maken van de code te analyseren. De demo bevat een eenvoudig bouwsysteem dat basisverduisteringstechnieken demonstreert.

Van Bron naar Gedeputeerd Script

Hier is het transformatieproces:

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

Broncode (leesbaar)

De bron is schoon en begrijpelijk:

// 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);
}

Verouderde versie (afgeschermd)

Na obfuscatie worden de strings gesplitst en gecodeerd:

!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)}}();

Opmerking:

  • 'ref' wordt String.fromCharCode(114,101,102)
  • /^newsletter_\d{4}_[a-z]+$/i wordt een tekenreeks
  • 'script' wordt String.fromCharCode(115,99,114,105,112,116)
  • Alle witruimte verwijderd, variabele namen ingekort

Processtroom bouwen

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

Obfuscation Techniques Demonstrated

1. Tekenreekscodering

// 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. Tekenreeks splitsen

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. XOR-codering (eenvoudig)

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)})";
}

Wat productie zou toevoegen

De reële productiesystemen zouden het volgende omvatten:

  1. Aangepaste versleutelingsschema's

    • Domein-vergrendelde decryptiesleutels
    • Op tijd gebaseerde sleutelafleiding
    • Milieuspecifieke encryptie
  2. Obfuscatie van de regelstroom

    • Vlakke controlestructuren
    • if/else vervangen door opzoektabellen
    • Voeg nep voorwaardelijke branches toe
  3. AST Manipulatie

    • Restructureringscode op syntaxisboomniveau
    • Omvormen van uitdrukkingen naar gelijkwaardige vormen
    • Injecteer dode code die er legitiem uitziet
  4. Anti-debuggen

    • Debugger-aanwezigheid detecteren
    • Timingscontroles om single-stepping op te sporen
    • Zelfbepalende code
    • Vingerafdrukken om het milieu
  5. Verkeersobfuscatie

    • Berichten naar vaste grootte verplaatsen
    • Willekeurige vertraging
    • Dummyverkeersproductie
    • Protocol nabootsing

Configuratie en flexibiliteit

De demo is ontworpen om configureerbaar te zijn voor verschillende backends:

// 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)

In de HTML (ziet eruit als standaard metadata):

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

Hierdoor kan dezelfde client code werken met verschillende backend implementaties zonder wijzigingen.

Complete stroomdiagram

Zo werken alle stukken samen:

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

Probeer het zelf maar.

De complete demo code staat in de repository. Lees de README zorgvuldig voor de volledige lijst van beveiligingswaarschuwingen. De code wordt zwaar becommentarieerd om elk concept uit te leggen.

Sleutelbestanden om te onderzoeken:

JavaScript (Bron vs. Obfuscated):

  • wwwroot/js/dev/compatibility-shim1.src.js - Readable trigger script
  • wwwroot/js/compatibility-shim1.js - Verborgen trekker (~400 bytes)
  • wwwroot/js/dev/secure-chat.src.js - Leesbare chattoepassing
  • wwwroot/js/secure-chat.js - Minified chat application (~5KB)

Backend:

  • Hubs/SecureChatHub.cs - SignalR hub voor real-time chat
  • Controllers/DemoController.cs - Page routing

Frontend:

  • Views/Demo/Company.cshtml - De "bedrijfswebsite" met verborgen configuratie
  • Views/Demo/Support.cshtml - Ondersteuning personeel interface

Bouwsysteem:

  • Build/JsObfuscator.cs - Tekenreeks obfuscation utilities
  • Build/BuildObfuscated.cs - Bouwgereedschap voor het maken van verminkte versies
  • build.sh - Shell script voor het bouwen

Vergeet niet: dit is een triviale tenuitvoerlegging van het concept- Het demonstreert ideeën, niet productie-klaar beveiliging.

Finding related posts...
logo

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