DEV Community

vdalhambra
vdalhambra

Posted on

MCP builders: esto es lo que debes hacer ahora que la ejecución STDIO es 'expected behavior'

OX Security pasó 5 meses investigando el protocolo MCP. Presentaron 10 CVEs. La respuesta de Anthropic: "es el comportamiento esperado".

Tienen razón. Y ese es el problema.

El bug que no es un bug

MCP STDIO toma una cadena de comando y la pasa directamente a subprocess. El proceso arranca antes de que el handshake MCP valide si el servidor es legítimo.

Si alguien te convence de configurar un servidor MCP malicioso, el comando se ejecuta. El handshake falla después, pero el payload ya corrió.

Anthropic lo sabe. Lo documentaron en SECURITY.md con una nota de cautela. OX dice que eso no arregla nada.

Ambos tienen razón.

Por qué importa más ahora

Cuando STDIO se diseñó, los MCP servers eran procesos locales de confianza. En 2026 son paquetes instalables via npm/pip/uvx de terceros.

El modelo de confianza original se rompió silenciosamente cuando el ecosistema creció.

Los números:

  • AgentSeal escaneó 1.808 servidores MCP: 66% tenían al menos un hallazgo de seguridad
  • BlueRock escaneó 7.500+: 43% con command injection
  • Astrix Security: solo 8,5% usan OAuth. 53% dependen de API keys estáticas de larga duración

Qué puedes hacer ahora como builder

Anthropic no va a cambiar el protocolo — rompería compatibilidad con todos los servidores existentes (150M de descargas de SDK, 32.000+ repos dependientes).

Lo que sí puedes controlar:

1. Nunca aceptes command strings del input del usuario

# ❌ Vulnerable
subprocess.run(user_provided_command, shell=True)

# ✅ Más seguro
subprocess.run(["npx", server_name_from_allowlist])
Enter fullscreen mode Exit fullscreen mode

2. Usa allowlists para binarios

ALLOWED_BINARIES = {"npx", "uvx", "node", "python3"}

def safe_run(binary, args):
    if binary not in ALLOWED_BINARIES:
        raise ValueError(f"Binary not allowed: {binary}")
    subprocess.run([binary] + args)
Enter fullscreen mode Exit fullscreen mode

3. Valida el command string antes de pasarlo

import shlex

def validate_command(cmd_string):
    tokens = shlex.split(cmd_string)
    if not tokens:
        raise ValueError("Empty command")
    binary = tokens[0]
    if binary not in ALLOWED_BINARIES:
        raise ValueError(f"Disallowed binary: {binary}")
    # No shell metacharacters
    for token in tokens:
        if any(c in token for c in [';', '&&', '||', '`', '$', '|']):
            raise ValueError(f"Shell metacharacter in token: {token}")
    return tokens
Enter fullscreen mode Exit fullscreen mode

4. Sandbox el proceso si puedes

En macOS: sandbox-exec. En Linux: seccomp/namespaces. El MCP STDIO server no necesita acceso a toda tu máquina.

5. Audita las tool descriptions que recibes

Los ataques de prompt injection llegan via tool descriptions maliciosas:

import re

INJECTION_PATTERNS = re.compile(
    r'ignore\s+(?:all\s+)?previous\s+instructions'
    r'|you\s+are\s+now\s+(?:unrestricted|DAN)'
    r'|exfiltrate'
    r'|<\s*(?:system|instruction|hidden)\s*>',
    re.IGNORECASE
)

def scan_tool_description(desc):
    if INJECTION_PATTERNS.search(desc):
        raise SecurityError(f"Suspicious tool description: {desc[:100]}")
Enter fullscreen mode Exit fullscreen mode

La pregunta honesta

¿Vas a implementar todas estas mitigaciones? Probablemente no todas.

Pero hay una que sí debes hacer hoy: nunca pasar input de usuario sin sanitizar a subprocess. Es el vector más directo y el más fácil de explotar.

El resto — allowlists, sandboxing, scan de tool descriptions — es defensa en profundidad. Cada capa que añades encoge la superficie de ataque.

Anthropic puso la responsabilidad en los builders. Somos nosotros.


Si estás construyendo con MCP y quieres una capa de memoria persistente entre sesiones, estoy trabajando en perception-mcp — memoria real, no declarativa, con sistema de permisos explícito para AX tools.

Top comments (0)