Back to "Demo Implementación: Una prueba trivial de concepto"

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

Demo Implementación: Una prueba trivial de concepto

Thursday, 20 November 2025

Para ilustrar estos conceptos en acción, he creado un proyecto de demostración que muestra cómo tal sistema podría funcionar. Se trata de una aplicación trivial con fines educativos únicamente e incluye deliberadamente una seguridad mínima para mantener el código legible y comprensible.

Advertencia crítica sobre la demostración

El código demo tiene MUCHAS debilidades de seguridad y NO es adecuado para ningún uso en el mundo real. Está diseñado para demostrar conceptos, no para ser desplegado. Vea la lista completa de problemas de seguridad en el README de la demo.

Estructura del proyecto de demostración

La demo es un proyecto aparte de ASP.NET Core 9.0 en el repositorio en Mostlylucid.SecureChat.Demo/ con los siguientes componentes:

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

Cómo ejecutar la demostración

  1. Clonar el repositorio y navegar a Mostlylucid.SecureChat.Demo
  2. Ejecutar dotnet build && dotnet run
  3. Abrir navegador a http://localhost:5000/Demo/Company
  4. Añadir parámetro de activación: ?ref=newsletter_2025_jan
  5. Introduzca la palabra clave cuando se le solicite: SAFE2025
  6. En otra pestaña, abrir /Demo/Support responder como personal de apoyo

El pequeño guión de activación

Aquí está el código real de compatibility-shim1.js - note lo pequeño e inocuo que es:

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

Este script es sólo ~1KB y hace dos cosas legítimas (comprobación de compatibilidad del navegador) antes de comprobar el disparador. Para cualquier persona que inspeccione el código, parece un ayudante estándar de polillenado.

El Hub de la SeñalR

El motor utiliza SignalR para la comunicación bidireccional en tiempo real. Aquí está la estructura de hub simplificada:

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

El chat cliente-side

Cuando se activa, el módulo de chat crea una ventana modal con una cuenta regresiva de autenticación de 30 segundos:

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 la autenticación falla o se apaga, el sistema redirige a una URL de reserva (almacenada en una etiqueta meta oculta en la página):

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

Lo que muestra la demostración

Conceptos básicos ilustrados:

  1. Activador oculto: Parámetro URL ?ref=newsletter_2025_jan Parece seguimiento de marketing
  2. Carga dinámica: El módulo de chat sólo se carga cuando se activa, manteniendo la carga útil inicial pequeña
  3. Auth limitado por el tiempo: La ventana de 30 segundos impide el sondeo indefinido
  4. Fallo plausible: Autenticación fallida parece un widget de soporte roto
  5. Comunicación en tiempo real: SignalR activa el chat bidireccional
  6. Separación de las preocupaciones: La interfaz del personal de apoyo está completamente separada

Lo que la demostración no muestra:

El sistema de producción tenía características mucho más sofisticadas no incluidas en la demo:

  • Encriptación real (mensajes enviados en texto plano en demo)
  • Código obfuscado/minificado (el código demo es legible)
  • Acolchado de tráfico y aleatorización del tiempo
  • Protocolos de intercambio de claves seguros
  • Rotación del token de sesión
  • Limitación de las tasas y prevención de los abusos
  • Registro completo de las auditorías
  • Medidas anti-forensics
  • Y docenas de otras capas de seguridad...

El Sistema de Construcción: Obfuscación y Minificación

Un aspecto crítico de los sistemas esteganográficos es hacer el código difícil de analizar. La demo incluye un sistema de construcción simple que demuestra técnicas básicas de obfuscación.

De la fuente al script desplegado

Aquí está el proceso de transformación:

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

Código fuente (lectura)

La fuente es limpia y comprensible:

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

Versión obsoleta (desplegada)

Después de la ofuscación, las cadenas se dividen y codifican:

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

Aviso:

  • 'ref' se convierte en String.fromCharCode(114,101,102)
  • /^newsletter_\d{4}_[a-z]+$/i se convierte en una matriz de caracteres
  • 'script' se convierte en String.fromCharCode(115,99,114,105,112,116)
  • Todos los espacios en blanco eliminados, nombres variables acortados

Construir flujo de proceso

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

Técnicas de ofuscación demostradas

1. Codificación de cadenas

// 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. Dividición de cuerdas

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. Codificación 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)})";
}

¿Qué añadiría la producción?

Los sistemas de producción reales incluirían:

  1. Esquemas de cifrado personalizados

    • Teclas de descifrado bloqueadas por dominio
    • derivación de claves en función del tiempo
    • Encriptación específica para el medio ambiente
  2. Obfuscación de flujo de control

    • Estructuras de control aplanadas
    • Reemplazar if/else con tablas de búsqueda
    • Añadir ramas condicionales falsas
  3. Manipulación AST

    • Reestructurar el código a nivel del árbol de sintaxis
    • Transformar expresiones a formas equivalentes
    • Inyectar código muerto que parezca legítimo
  4. Antidepuración

    • Detectar la presencia del depurador
    • Controles de tiempo para detectar un solo paso
    • Código de automodificación
    • Toma de huellas dactilares en el medio ambiente
  5. Obfuscación del tráfico

    • Mensajes de Pad a tamaños fijos
    • Retrasos aleatorios en el tiempo
    • Generación de tráfico de basura
    • Imitación de protocolo

Configuración y flexibilidad

La demo está diseñada para ser configurable para diferentes motores:

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

En el HTML (parecen metadatos estándar):

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

Esto permite que el mismo código cliente funcione con diferentes implementaciones de backend sin modificación.

Diagrama de flujo completo

Así es como todas las piezas trabajan juntas:

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

Pruébalo tú mismo.

El código completo de demostración está en el repositorio. Lea el README cuidadosamente para obtener la lista completa de advertencias de seguridad. El código es muy comentado para explicar cada concepto.

Archivos clave para examinar:

JavaScript (Fuente vs. Obfuscado):

  • wwwroot/js/dev/compatibility-shim1.src.js - Guión de activación legible
  • wwwroot/js/compatibility-shim1.js - Detonador obfuscado (~400 bytes)
  • wwwroot/js/dev/secure-chat.src.js - Aplicación de chat legible
  • wwwroot/js/secure-chat.js - Aplicación de chat minimizada (~5KB)

Motor:

  • Hubs/SecureChatHub.cs - SignalR hub para chatear en tiempo real
  • Controllers/DemoController.cs - Enrutamiento de páginas

Interfaz:

  • Views/Demo/Company.cshtml - El "sitio web de la empresa" con configuración oculta
  • Views/Demo/Support.cshtml - Interfaz del personal de apoyo

Construir sistema:

  • Build/JsObfuscator.cs - Utilidades de obfuscación de cuerdas
  • Build/BuildObfuscated.cs - Construir herramienta para crear versiones minificadas
  • build.sh - Guión Shell para la construcción

Recuerde: Esto es un aplicación trivial del concepto. Demuestra ideas, no seguridad preparada para la producción.

logo

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