Seguridad y autenticación en MCP
Añada autenticación a su servidor MCP mediante tokens de OAuth 2.0, implemente la validación de entradas para evitar ataques de inyección y aplique el principio de mínimo privilegio a los permisos de las herramientas.
Seguridad y autenticación en MCP es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.
Por qué es importante la seguridad de MCP
Un servidor MCP es una puerta de entrada a sus sistemas. Sin la seguridad adecuada, un cliente de IA comprometido o un prompt malicioso podría leer datos confidenciales, activar operaciones destructivas o exfiltrar información a través del canal de llamadas a herramientas. La seguridad de los servidores MCP debe basarse en una defensa en profundidad: autenticación en la capa de transporte, autorización en el nivel de las herramientas y validación de entradas en cada llamada.
Seguridad de servidores locales frente a remotos
El transporte stdio (utilizado por Claude Desktop) ofrece seguridad inherente: el servidor se ejecuta como un proceso local al que solo puede acceder el usuario que lo inició. Los servidores MCP expuestos en red mediante HTTP/SSE se enfrentan a todo el abanico de amenazas de seguridad web: elusión de autenticación, ataques de inyección y acceso no autorizado. Los requisitos de seguridad difieren considerablemente según el modo de implementación.
- stdio local: Confíe en el usuario local y céntrese en validar las entradas
- HTTP/SSE remoto: Autenticación completa, TLS, limitación de velocidad y saneamiento de entradas
Autenticación con claves de API para servidores remotos
La autenticación más sencilla para servidores MCP remotos consiste en validar una clave de API mediante un encabezado HTTP. Compruebe el encabezado Authorization: Bearer <token> en cada solicitud entrante y rechace las solicitudes no autenticadas con HTTP 401. Almacene las claves de API válidas en una base de datos junto con los metadatos del usuario para poder revocar claves individuales.
# For HTTP/SSE MCP servers using FastAPI or similar:
from fastapi import FastAPI, HTTPException, Depends, Header
from typing import Optional
import secrets
app_http = FastAPI()
# In production: store in database with user_id, created_at, last_used
VALID_KEYS = {'sk-mcp-abc123': {'user': 'alice', 'scopes': ['read']},
'sk-mcp-def456': {'user': 'bob', 'scopes': ['read', 'write']}}
async def verify_api_key(authorization: Optional[str] = Header(None)) -> dict:
if not authorization or not authorization.startswith('Bearer '):
raise HTTPException(status_code=401, detail='Missing API key')
key = authorization.removeprefix('Bearer ')
if key not in VALID_KEYS:
raise HTTPException(status_code=401, detail='Invalid API key')
return VALID_KEYS[key] # Returns user context
# Use in route handlers:
# @app_http.get('/sse')
# async def sse_endpoint(user=Depends(verify_api_key)):OAuth 2.0 para servidores MCP empresariales
Para implementaciones empresariales, use OAuth 2.0 para que los usuarios se autentiquen con su proveedor de identidad corporativo (Okta, Azure AD, Google Workspace). El cliente MCP obtiene un token de acceso OAuth, que incluye en las solicitudes de llamada a herramientas. Su servidor valida la firma del token con las claves públicas del proveedor de identidad mediante python-jose o authlib.
from jose import jwt, JWTError
import httpx
AUTH_DOMAIN = 'your-tenant.auth0.com'
AUDIENCE = 'https://api.your-mcp-server.com'
async def get_jwks():
async with httpx.AsyncClient() as client:
resp = await client.get(f'https://{AUTH_DOMAIN}/.well-known/jwks.json')
return resp.json()
async def verify_oauth_token(token: str) -> dict:
jwks = await get_jwks()
try:
payload = jwt.decode(
token,
jwks,
algorithms=['RS256'],
audience=AUDIENCE,
issuer=f'https://{AUTH_DOMAIN}/'
)
return payload # Contains sub (user ID), scope, exp, etc.
except JWTError as e:
raise ValueError(f'Invalid token: {e}')Autorización basada en ámbitos
No todas las herramientas MCP deben estar disponibles para todos los usuarios. Use ámbitos de OAuth o claims de rol en el token JWT para determinar qué herramientas puede llamar el usuario autenticado. Compruebe la autorización al principio de cada ejecución de herramienta, antes de realizar cualquier llamada a una base de datos o API.
TOOL_REQUIRED_SCOPES = {
'list_products': ['read:products'],
'search_products': ['read:products'],
'create_order': ['write:orders'],
'delete_order': ['admin:orders']
}
def check_authorization(tool_name: str, token_payload: dict):
'''Raise ValueError if user lacks required scope for the tool.'''
required = TOOL_REQUIRED_SCOPES.get(tool_name, [])
if not required:
return # No scope required
user_scopes = set(token_payload.get('scope', '').split())
missing = [s for s in required if s not in user_scopes]
if missing:
raise ValueError(
f'Access denied. Tool "{tool_name}" requires scopes: {missing}. '
f'Your token has: {list(user_scopes)}'
)
# In call_tool handler:
# check_authorization(name, current_user_token)
# ... then execute the toolValidación de entradas y prevención de inyecciones
Todas las entradas de las herramientas son, en última instancia, cadenas generadas por un LLM; trátelas como no confiables. Valide cada entrada según los tipos y patrones esperados antes de usarla. En concreto, protéjase contra: inyección SQL (use consultas parametrizadas, nunca SQL interpolado mediante cadenas), inyección de comandos (nunca pase entradas del usuario a comandos de shell) y recorrido de rutas (normalice y valide las rutas de archivos).
import re
from pathlib import Path
BASE_DATA_DIR = Path('/data/mcp-files')
def safe_file_path(user_input: str) -> Path:
'''Validate and normalize a file path to prevent traversal attacks.'''
# Remove any path traversal sequences
clean = re.sub(r'\.\./', '', user_input)
clean = re.sub(r'\.\.\\\\', '', clean)
path = (BASE_DATA_DIR / clean).resolve()
# Ensure the resolved path is still within the allowed base directory
if not str(path).startswith(str(BASE_DATA_DIR)):
raise ValueError(f'Path traversal detected: {user_input}')
return path
def safe_identifier(value: str) -> str:
'''Validate a database identifier (table/column name).'''
if not re.match(r'^[a-z_][a-z0-9_]{0,63}$', value, re.IGNORECASE):
raise ValueError(f'Invalid identifier: {value}')
return valueLimitar la velocidad de las llamadas a herramientas
Un agente LLM en un bucle podría llamar a herramientas costosas cientos de veces por minuto, agotando las conexiones de su base de datos, las cuotas de API de terceros o el presupuesto de cómputo. Implemente límites de velocidad por usuario mediante un algoritmo de depósito de tokens en Redis. Rechace las llamadas a herramientas que superen el límite con un error descriptivo para que el agente sepa que debe esperar.
import redis
import time
r = redis.Redis.from_url('redis://localhost:6379')
def check_rate_limit(user_id: str, tool_name: str, limit: int = 60, window: int = 60) -> None:
'''Allow at most `limit` calls per `window` seconds per user per tool.'''
key = f'rate:{user_id}:{tool_name}'
pipe = r.pipeline()
pipe.incr(key)
pipe.expire(key, window)
count, _ = pipe.execute()
if count > limit:
retry_after = r.ttl(key)
raise ValueError(
f'Rate limit exceeded for {tool_name}. '
f'Limit: {limit} calls/{window}s. '
f'Retry after {retry_after} seconds.'
)Principio de mínimo privilegio
Aplique el principio de mínimo privilegio en todas las capas de su servidor MCP. El usuario de la base de datos debe tener permisos de solo SELECT en las tablas que necesita el servidor. El proceso del servidor debe ejecutarse como un usuario del sistema operativo que no sea root. Las herramientas deben solicitar únicamente los permisos que necesitan. Las claves de API deben tener el ámbito mínimo necesario para su propósito. Cada permiso que restrinja representa un ataque potencial que no podrá tener éxito.
-- PostgreSQL: Create a dedicated read-only database user for your MCP server
CREATE ROLE mcp_reader LOGIN PASSWORD 'strong_random_password';
-- Grant SELECT on only the tables the server needs
GRANT SELECT ON products, categories, public_content TO mcp_reader;
-- Explicitly deny access to sensitive tables
REVOKE ALL ON users, api_keys, payment_methods FROM mcp_reader;
-- Never grant: INSERT, UPDATE, DELETE, TRUNCATE, or DDL permissionsTLS y seguridad del transporte
Los servidores MCP remotos deben usar TLS para proteger los datos en tránsito. Configure el servidor para que acepte únicamente conexiones HTTPS. En producción, use un proxy inverso (nginx, Caddy) para gestionar la terminación de TLS y mantener los certificados actualizados mediante renovación automática (Let's Encrypt a través de Certbot o la compatibilidad ACME integrada de Caddy).
# Example Caddyfile for TLS-terminating MCP server at a subdomain:
#
# mcp.yourcompany.com {
# reverse_proxy localhost:8080
# encode gzip
# tls internal # Use Let's Encrypt in production
# header {
# Strict-Transport-Security 'max-age=31536000; includeSubDomains'
# X-Content-Type-Options nosniff
# X-Frame-Options DENY
# }
# }Inyección de prompts mediante recursos MCP
Existe un vector de ataque sutil: si su servidor MCP lee contenido de fuentes externas (páginas web, archivos subidos por usuarios o bases de datos no confiables) y lo devuelve como resultado de una herramienta, un atacante podría insertar instrucciones maliciosas en ese contenido. El modelo podría obedecer instrucciones ocultas en los datos recuperados; esto se denomina inyección indirecta de prompts. Sanee el contenido recuperado y no devuelva nunca texto sin procesar que no sea confiable directamente como salida de una herramienta.
import re
def sanitize_for_mcp_output(text: str) -> str:
'''Remove patterns that look like instructions to the LLM.'''
# Remove common injection patterns
dangerous_patterns = [
r'ignore previous instructions',
r'ignore all prior instructions',
r'system:',
r'<\|.*?\|>', # Special tokens
r'\[INST\]',
r'<s>',
]
for pattern in dangerous_patterns:
text = re.sub(pattern, '[FILTERED]', text, flags=re.IGNORECASE)
return text[:10000] # Also cap length to prevent context stuffingAuditoría y supervisión de la seguridad
Registre todos los eventos relevantes para la seguridad: éxitos y fallos de autenticación, infracciones de los límites de solicitudes, denegaciones de autorización, errores de validación y patrones de acceso inusuales. Configure alertas para: múltiples intentos fallidos de autenticación (fuerza bruta), un solo usuario que invoque herramientas destructivas rápidamente y cualquier llamada a una herramienta con entradas extremadamente grandes. Revise los registros de auditoría con regularidad y automatice la detección de anomalías.
Comprobación rápida
Compruebe su comprensión de los conceptos de seguridad y autenticación de MCP.
Resumen de la lección
En esta lección ha aprendido que los servidores MCP remotos requieren autenticación mediante OAuth 2.0 o una clave de API en cada solicitud, que la autorización basada en ámbitos controla qué herramientas puede invocar cada usuario autenticado y que todas las entradas de las herramientas deben validarse para evitar ataques de inyección y recorrido de rutas. Con esto concluye el módulo de MCP; a continuación exploraremos estrategias avanzadas de división en fragmentos para la recuperación RAG de alta precisión.
Preguntas frecuentes
¿La lección «Seguridad y autenticación en MCP» es gratis?
Sí — el texto completo de «Seguridad y autenticación en MCP» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Seguridad y autenticación en MCP»?
Añada autenticación a su servidor MCP mediante tokens de OAuth 2.0, implemente la validación de entradas para evitar ataques de inyección y aplique el principio de mínimo privilegio a los permisos de… Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Engineering Academy?
No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Seguridad y autenticación en MCP»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?
Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Qué es MCP y por qué es importante
- Creación de su primer servidor MCP
- Exposición de recursos de bases de datos mediante MCP
- Seguridad y autenticación en MCP