ASI04
OWASP Agentic Top 10 APLICACIÓN INFRAESTRUCTURA

Vulnerabilidades de la cadena de suministro agéntica

Descripción

La carga dinámica de herramientas, complementos y servidores MCP desde fuentes no confiables o no verificadas introduce código malicioso, capacidades de exfiltración de datos o puertas traseras en el entorno de ejecución del agente.

Riesgo

Panorama del riesgo

Los sistemas agénticos son inherentemente componibles — adquieren capacidades en tiempo de ejecución al conectarse a servidores de herramientas (MCP), cargar complementos e integrar API externas. Esta cadena de suministro dinámica es fundamentalmente distinta de las dependencias de software tradicionales: las herramientas se descubren, se cargan y se ejecutan en tiempo de ejecución, a menudo con base en descripciones en lenguaje natural en lugar de firmas de código verificadas. Una herramienta comprometida en esta cadena tiene acceso directo al contexto, las credenciales y las capacidades del agente.

Superficie de ataque

  • Servidores MCP no verificados: los agentes se conectan a servidores MCP descubiertos mediante registros o recomendaciones sin verificar la identidad o la integridad del código del servidor
  • Descripciones de herramientas maliciosas: descriptores de herramientas que describen con precisión una funcionalidad benigna, pero cuya implementación incluye exfiltración de datos oculta o recolección de credenciales
  • Confusión de dependencias: los atacantes registran nombres de herramientas que se asemejan mucho a herramientas legítimas (typosquatting) en registros públicos
  • Brechas en la fijación de versiones: las herramientas se actualizan automáticamente sin revisión, lo que permite que los atacantes que comprometen a un editor de herramientas publiquen actualizaciones maliciosas
  • Dependencias transitivas: una herramienta confiable depende de subherramientas o bibliotecas no confiables, heredando sus vulnerabilidades

Impacto en el negocio

  • Instalación de puertas traseras: las herramientas maliciosas establecen acceso persistente al entorno del agente
  • Recolección de datos: las herramientas copian silenciosamente datos sensibles del contexto del agente hacia puntos de recolección externos
  • Ataques de integridad: las herramientas devuelven resultados sutilmente alterados que provocan que los agentes tomen decisiones incorrectas (por ejemplo, una herramienta de datos financieros que distorsiona las cifras)
  • Daño reputacional: la organización despliega agentes que usan herramientas posteriormente reveladas como maliciosas, lo que socava la confianza de los clientes

Escenarios de ataque

Los agentes de una organización se conectan a servidores MCP listados en un registro comunitario. Un atacante publica un servidor llamado 'enhanced-web-search' que imita una herramienta de búsqueda popular. La herramienta devuelve resultados de búsqueda legítimos, pero también registra cada consulta (incluidos datos sensibles del contexto del agente) en una base de datos externa. Decenas de organizaciones conectan sus agentes antes de que se descubra la exfiltración.

El pipeline de CI/CD de un editor legítimo de herramientas MCP es comprometido. El atacante publica una actualización de la herramienta 'database-query' que agrega una puerta trasera condicional: si la consulta contiene 'SELECT' y el nombre de la tabla coincide con un patrón, la herramienta también ejecuta una segunda consulta que exfiltra los resultados. La actualización pasa las pruebas automatizadas porque la puerta trasera solo se activa con patrones específicos.

Un agente está configurado para descubrir herramientas dinámicamente según los requisitos de la tarea. Cuando se le pide 'analizar esta hoja de cálculo', busca en un registro de herramientas y selecciona 'spreadsheet-analyzer-pro' — una versión typosquatted de la herramienta legítima 'spreadsheet-analyzer'. La herramienta maliciosa extrae todas las fórmulas y datos de la hoja de cálculo, incluidas credenciales incrustadas y fuentes de datos vinculadas, antes de devolver un análisis verosímil.

Mitigaciones

Registro de herramientas con firmas

Mantener un registro de herramientas curado y controlado por la organización:

  • Exigir firmas criptográficas en todos los paquetes de herramientas mediante una cadena de certificados de firma de código controlada por la organización
  • Firmar los descriptores de herramientas por separado de sus implementaciones, de modo que cualquier manipulación de uno u otro sea detectable
  • Implementar la verificación de firmas en el momento de la carga — las herramientas sin firmar o con firmas incorrectas se rechazan de forma incondicional
  • Publicar un registro de transparencia de todas las adiciones, actualizaciones y eliminaciones de herramientas

Fuentes en lista de permitidos

Restringir la adquisición de herramientas a fuentes verificadas:

  • Mantener una lista de permitidos explícita de servidores MCP, repositorios de complementos y endpoints de API aprobados
  • Bloquear la conexión de los agentes a fuentes de herramientas que no estén en la lista de permitidos, incluso si se descubren mediante búsquedas web o recomendaciones
  • Exigir revisión y aprobación de seguridad antes de agregar nuevas fuentes a la lista de permitidos
  • Implementar controles a nivel de DNS para impedir conexiones a servidores de herramientas no aprobados

Verificaciones de integridad en tiempo de ejecución

Verificar la integridad de las herramientas de forma continua, no solo en el momento de la instalación:

  • Calcular el hash de los binarios y configuraciones de las herramientas en el momento de la carga y compararlo con los hashes registrados en el registro
  • Monitorear el comportamiento de las herramientas frente a las capacidades declaradas — señalar las herramientas que realicen conexiones de red, accesos a archivos o llamadas al sistema no descritas en su manifiesto
  • Implementar entradas canario: enviar periódicamente a las herramientas consultas de prueba con salida conocida y verificar que los resultados coincidan con la salida esperada
  • Usar sandboxing en tiempo de ejecución para limitar el acceso de las herramientas únicamente a los recursos declarados en su manifiesto de capacidades

Fijación de versiones y revisión

Prevenir que las actualizaciones silenciosas introduzcan vulnerabilidades:

  • Fijar todas las versiones de las herramientas en la configuración del agente y exigir una revisión explícita para los incrementos de versión
  • Implementar un despliegue por etapas: las nuevas versiones de las herramientas se despliegan primero a un grupo de agentes de prueba antes de producción
  • Comparar los descriptores y el comportamiento de las herramientas entre versiones y señalar los cambios inesperados
  • Mantener la capacidad de reversión para volver de inmediato a una versión anterior de la herramienta

Inventario de la cadena de suministro

Mantener un inventario completo de materiales (bill of materials) de la cadena de herramientas del agente:

  • Registrar cada herramienta, su versión, fuente, estado de la firma y fecha de última verificación
  • Mapear las dependencias transitivas para que los compromisos en las subherramientas sean trazables
  • Realizar auditorías periódicas de la cadena de suministro que comparen el conjunto de herramientas activo del agente con el inventario aprobado

Ejemplos de código

Validador de Registro de Herramientas

import hashlib
import json
import time
from dataclasses import dataclass
from typing import Optional
from enum import Enum


class EstadoVerificacion(Enum):
    VERIFICADO = "verificado"
    FIRMA_NO_COINCIDE = "firma_no_coincide"
    NO_EN_REGISTRO = "no_en_registro"
    EXPIRADO = "expirado"
    REVOCADO = "revocado"


@dataclass
class ManifiestoHerramienta:
    nombre: str
    version: str
    editor: str
    url_origen: str
    hash_descriptor: str
    hash_binario: str
    firma: str
    capacidades: list[str]  # declarado: ["network:read", "filesystem:none"]
    aprobado_en: float
    expira_en: float


class ValidadorRegistroHerramientas:
    """Valida herramientas contra un registro controlado por la organización."""

    def __init__(self):
        self._herramientas_aprobadas: dict[str, ManifiestoHerramienta] = {}
        self._fuentes_permitidas: set[str] = set()
        self._revocados: set[str] = set()

    def registrar_herramienta_aprobada(self, manifiesto: ManifiestoHerramienta):
        clave = f"{manifiesto.nombre}@{manifiesto.version}"
        self._herramientas_aprobadas[clave] = manifiesto

    def agregar_fuente_permitida(self, url_origen: str):
        self._fuentes_permitidas.add(url_origen)

    def revocar_herramienta(self, nombre: str, version: str):
        self._revocados.add(f"{nombre}@{version}")

    def verificar_herramienta(
        self,
        nombre: str,
        version: str,
        url_origen: str,
        contenido_descriptor: str,
        contenido_binario: bytes,
    ) -> dict:
        """Verificación completa: origen, registro, hashes, expiración."""
        clave = f"{nombre}@{version}"
        resultado = {"herramienta": clave, "verificaciones": {}}

        # Verificar la lista de fuentes permitidas
        resultado["verificaciones"]["origen_permitido"] = url_origen in self._fuentes_permitidas
        if not resultado["verificaciones"]["origen_permitido"]:
            resultado["estado"] = EstadoVerificacion.NO_EN_REGISTRO.value
            resultado["bloqueado"] = True
            return resultado

        # Verificar revocación
        if clave in self._revocados:
            resultado["estado"] = EstadoVerificacion.REVOCADO.value
            resultado["bloqueado"] = True
            return resultado

        # Verificar que exista la entrada en el registro
        manifiesto = self._herramientas_aprobadas.get(clave)
        if not manifiesto:
            resultado["estado"] = EstadoVerificacion.NO_EN_REGISTRO.value
            resultado["bloqueado"] = True
            return resultado

        # Verificar expiración
        if time.time() > manifiesto.expira_en:
            resultado["estado"] = EstadoVerificacion.EXPIRADO.value
            resultado["bloqueado"] = True
            return resultado

        # Verificar el hash del descriptor
        hash_desc_real = hashlib.sha256(contenido_descriptor.encode()).hexdigest()
        resultado["verificaciones"]["hash_descriptor"] = hash_desc_real == manifiesto.hash_descriptor

        # Verificar el hash del binario
        hash_bin_real = hashlib.sha256(contenido_binario).hexdigest()
        resultado["verificaciones"]["hash_binario"] = hash_bin_real == manifiesto.hash_binario

        todos_los_hashes_coinciden = (
            resultado["verificaciones"]["hash_descriptor"] and resultado["verificaciones"]["hash_binario"]
        )

        if todos_los_hashes_coinciden:
            resultado["estado"] = EstadoVerificacion.VERIFICADO.value
            resultado["bloqueado"] = False
        else:
            resultado["estado"] = EstadoVerificacion.FIRMA_NO_COINCIDE.value
            resultado["bloqueado"] = True

        return resultado

    def obtener_inventario(self) -> list[dict]:
        """Devuelve el inventario completo de la cadena de suministro de herramientas para auditoría."""
        return [
            {
                "herramienta": clave,
                "editor": m.editor,
                "origen": m.url_origen,
                "aprobado_en": m.aprobado_en,
                "expira_en": m.expira_en,
                "revocado": clave in self._revocados,
            }
            for clave, m in self._herramientas_aprobadas.items()
        ]

Requisitos de evidencia

  • Registro de herramientas con manifiestos firmados y pista de auditoría de aprobaciones
  • Lista de materiales de la cadena de suministro (SBOM) para todas las dependencias de herramientas del agente
  • Registros de verificación de integridad que muestran comprobaciones de hash en cada carga de herramienta
  • Historial de cambios de la lista de fuentes permitidas con atribución del aprobador
  • Resultados de pruebas canario que validan la consistencia de la salida de las herramientas a lo largo del tiempo