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