Back to "Demon toteutus: Triviaal Proof of Concept"

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

History JavaScript Security

Demon toteutus: Triviaal Proof of Concept

Thursday, 20 November 2025

Näiden käsitteiden havainnollistamiseksi olen luonut demonstraatioprojektin, joka osoittaa, miten tällainen järjestelmä voisi toimia. Tämä on vähäpätöinen toteutus vain opetustarkoituksiin ja tietoisesti sisältää minimaalisen turvallisuuden, jotta koodi pysyy luettavissa ja ymmärrettävänä.

Kriittinen varoitus demosta

Demokoodissa on monia turvallisuuspuutteita, eikä se sovellu mihinkään todelliseen käyttöön. Se on suunniteltu osoittamaan käsitteitä, ei käytettäväksi. Katso täydellinen lista turvallisuusongelmista demon README-julkaisussa.

Demoprojektin rakenne

Demo on erillinen ASP.NET Core 9.0 -projekti arkistossa osoitteessa Mostlylucid.SecureChat.Demo/ jossa on seuraavat osat:

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

Demon pyörittäminen

  1. Kloonaaa arkisto ja navigoi Mostlylucid.SecureChat.Demo
  2. Juokse dotnet build && dotnet run
  3. Avaa selain http://localhost:5000/Demo/Company
  4. Lisää liipaisinparametri: ?ref=newsletter_2025_jan
  5. Syötä koodisana, kun se on annettu: SAFE2025
  6. Avaa toisessa välilehdessä /Demo/Support vastaa tukihenkilönä

Pieni aloituskirjoitus

Tässä on oikea koodi compatibility-shim1.js - huomaa, kuinka pieni ja harmiton se on:

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

Tämä skripti on vain ~1KB ja tekee kaksi laillista asiaa (selausyhteensopivuustarkistukset) ennen kuin tarkistaa laukaisimen. Jokaisesta koodia tarkastavasta henkilöstä se näyttää tavalliselta monitäyttöapulaiselta.

SignalR-hub

Backend käyttää SignalR:ää reaaliaikaiseen kaksisuuntaiseen viestintään. Tässä on yksinkertaistettu naparakenne:

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

Asiakas-side-chat

Kun chat-moduuli käynnistyy, se luo modaaliikkunan, jossa on 30 sekunnin varmennuslaskenta:

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

Jos varmennus epäonnistuu tai ajoittuu, järjestelmä ohjaa vara-osoitteen (tallentuu sivulla olevaan piilotettuun metatagiin):

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

Mitä demo osoittaa

Keskeisiä käsitteitä valaistiin:

  1. Piilolaukaisin: URL-parametri ?ref=newsletter_2025_jan näyttää markkinointijäljitykseltä
  2. Dynaaminen lataus: Chat-moduuli latautuu vain laukaistuna pitäen alkuperäisen hyötykuorman pienenä
  3. Aikalimiitteinen auth: 30 sekunnin ikkuna estää epämääräisen kokeilun
  4. Uskottava epäonnistuminen: Epäonnistunut auth näyttää rikkinäiseltä tukivihkolta
  5. Reaaliaikainen viestintä: SignalR mahdollistaa kahdenkeskisen chatin
  6. Huolien erottaminen: Tukihenkilöstön käyttöliittymä on täysin erillinen

Mitä Demo ei näytä:

Tuotantojärjestelmässä oli huomattavasti kehittyneempiä ominaisuuksia, jotka eivät sisältyneet demoon:

  • Varsinainen salaus (viestit lähetetään suorassa tekstissä demossa)
  • Obfusoitu/minifioitu koodi (demokoodi on luettavissa)
  • Liikennetyynyt ja ajoituksen sattumanvaraisuus
  • Turvalliset avaintenvaihtoprotokollat
  • Istunnon tunnuspyörre
  • Verokannan rajoittaminen ja väärinkäytön estäminen
  • Kattava auditointihakkuri
  • Forension vastaiset toimenpiteet
  • Ja kymmeniä muita turvakerroksia...

Rakennusjärjestelmä: Obfusation and Minification

Yksi kriittinen osa steganografisia järjestelmiä on se, että koodia on vaikea analysoida. demoon kuuluu yksinkertainen rakennusjärjestelmä, joka esittelee perusvajaustekniikoita.

Lähteestä käyttöönotettuun käsikirjoitukseen

Tässä muutosprosessi:

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

Lähdekoodi (luettavissa)

Lähde on puhdas ja ymmärrettävä:

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

Obfuscated Version (Käytössä)

Offfusoinnin jälkeen narut jaetaan ja koodataan:

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

Huomautus:

  • 'ref' tulee String.fromCharCode(114,101,102)
  • /^newsletter_\d{4}_[a-z]+$/i tulee hahmojoukko
  • 'script' tulee String.fromCharCode(115,99,114,105,112,116)
  • Kaikki whitespace poistettu, muuttujanimet lyhennetty

Rakenna prosessivirtaa

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

Havaitsemistekniikoita, jotka on osoitettu

1. Merkkijonokoodaus

// 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. String splitting

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-koodaus (yksinkertainen)

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

Mitä tuotanto lisäisi

Todellisiin tuotantojärjestelmiin kuuluisivat seuraavat:

  1. Mukautetut salausjärjestelmät

    • Verkkoalueen lukitut salauksen purkuavaimet
    • Aikaperusteinen avainjohtaminen
    • Ympäristökohtainen salaus
  2. Hallintavirran havahdus

    • Litteät ohjausrakenteet
    • Korvaa if/else hakutaulukolla
    • Lisää tekaistuja ehdollisia sivukonttoreita
  3. ASAT- manipulaatio

    • Restruktuurikoodi syntaksipuutasolla
    • Muunna ilmaukset vastaaviksi muodoiksi
    • Ruiskuta kuollut koodi, joka näyttää oikeutetulta
  4. Vianetsintävastaisuus

    • Tunnista debuggerin läsnäolo
    • Ajoitustarkastukset yksivaiheisuuden havaitsemiseksi
    • Itsemuokattava koodi
    • Ympäristön sormenjälkien ottaminen
  5. Liikennehaitat

    • Pad-viestejä kiinteisiin kokoihin
    • Satunnaiset ajoitusviiveet
    • Tylsää liikennettä
    • Protokollan jäljittely

Konfiguraatio ja joustavuus

Demo on suunniteltu konfiguroitaviksi eri taustakappaleita varten:

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

HTML:ssä (näyttää normaalilta metadatalta):

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

Tämä mahdollistaa saman asiakaskoodin toimimisen eri backend-toteutusten kanssa ilman muutoksia.

Täydennä virtauksen kaavio

Näin kaikki palaset toimivat yhdessä:

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

Kokeile itse

Täydellinen demokoodi on arkistossa. Lue README huolellisesti koko turvallisuusvaroitusten listaa varten. Koodia kommentoidaan paljon jokaisen konseptin selittämiseksi.

Avaintiedostot tutkittavana:

Javascript (Lähde vs. Offuscated):

  • wwwroot/js/dev/compatibility-shim1.src.js - Luettava laukaisinskripti
  • wwwroot/js/compatibility-shim1.js - Estynyt laukaisin (~400 tavua)
  • wwwroot/js/dev/secure-chat.src.js - Luettava chat-sovellus
  • wwwroot/js/secure-chat.js - Vähennetty chat-sovellus (~5KB)

Tausta:

  • Hubs/SecureChatHub.cs - SignalR-keskus reaaliaikaiseen chattiin
  • Controllers/DemoController.cs - Sivun reititys

Frontend:

  • Views/Demo/Company.cshtml - "Yhtiösivusto", jossa on piilotettu konfiguraatio
  • Views/Demo/Support.cshtml - Tukihenkilöstön rajapinta

Rakentamisjärjestelmä:

  • Build/JsObfuscator.cs - String obfusation apuohjelmat
  • Build/BuildObfuscated.cs - Rakenna työkalu minimoitujen versioiden luomiseen
  • build.sh - Shellin käsikirjoitus rakennukseen

Muista, että tämä on Käsitteen vähäpätöinen toteuttaminenSe on osoitus ideoista, ei tuotantovalmiista turvallisuudesta.

logo

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