# Las cargas de archivos de los agentes de IA necesitan límites estrictos

Un endpoint de carga es un canal de datos saliente, aunque los ingenieros lo llamen una función de adjuntos. Cuando un agente de IA puede adjuntar un registro, una exportación, una captura de pantalla o un archivo de cliente, una instrucción imprecisa como «envía esto a soporte» puede convertirse en una divulgación irreversible.

Las cargas de archivos de los agentes de IA necesitan límites estrictos para los bytes, el contenido y los receptores. Coloca esos límites en la acción que realiza la carga, no en el prompt del agente ni en la capacidad de un revisor para detectar un nombre de archivo incorrecto al final de un día agotador.

## Una acción de carga debe declarar qué puede salir

Un flujo de carga seguro empieza tratando cada archivo como un objeto con una finalidad declarada. La finalidad determina su tamaño máximo, los formatos aceptados, el receptor permitido, el tiempo de conservación y si una persona debe aprobar el envío. Si tu API acepta un cuerpo multipart arbitrario y una URL proporcionada por quien llama, has creado una ruta general de exfiltración de datos con una interfaz agradable para desarrolladores.

La diferencia que suele confundirse es entre recibir archivos de un cliente no confiable y enviar archivos en nombre de un agente. Las recomendaciones tradicionales sobre cargas se centran en proteger el servidor frente a archivos maliciosos. Las cargas de agentes también necesitan esa protección, pero el riesgo más inmediato es proteger los datos de un envío demasiado amplio. Un PDF puede ser perfectamente seguro para analizar y completamente inadecuado para entregarlo a un sistema de gestión de tickets.

Define finalidades de carga con nombre en lugar de una acción genérica como `upload_file`. Una finalidad como `diagnostic_bundle` puede permitir un paquete comprimido de soporte para un único receptor de soporte. Una finalidad como `invoice_export` puede permitir un CSV para el receptor de contabilidad. Ninguna de las dos debería aceptar una ruta, una URL de receptor y un archivo arbitrario en la misma solicitud.

Un contrato pequeño hace visible el límite:

```json
{
  "purpose": "diagnostic_bundle",
  "file_path": "/private/tmp/app-diagnostics-2025-03-08.zip",
  "destination_id": "support-case",
  "case_reference": "CASE-1842"
}
```

Quien llama elige una finalidad aprobada y proporciona los metadatos necesarios para la acción empresarial. El servicio de carga asigna `support-case` a un receptor que controla. No permite que quien llama sustituya ese receptor por una URL incluida en un comentario de incidencia, un documento o la respuesta de una herramienta.

Mantén bajo control las rutas de archivo. Un agente solo debería seleccionar archivos de directorios de preparación designados, o el servicio debería crear el adjunto a partir de entradas conocidas. Permitir que un agente indique cualquier ruta legible convierte una solicitud para adjuntar diagnósticos en una solicitud para leer archivos de configuración, material SSH, datos del navegador o la exportación de otro usuario.

## Los límites de tamaño necesitan dos mediciones

Un límite de archivo debe rechazar un cuerpo excesivo antes de que el servicio lo almacene, analice o reenvíe. Aplícalo en el borde HTTP mediante `Content-Length` cuando esté presente y cuenta los bytes durante la lectura, porque un cliente puede omitir o falsear ese encabezado.

El límite debe ajustarse a la finalidad. Un límite de 25 MB para un CSV de clientes y otro de 25 MB para un paquete de diagnóstico comprimido no son decisiones equivalentes. El CSV puede expandirse hasta ocupar mucha más memoria cuando lo lee un parser. El archivo comprimido puede descomprimirse hasta alcanzar varias veces su tamaño de transporte. Define un límite para el tamaño en tránsito y otro para el tamaño procesado.

No permitas que un agente divida un archivo rechazado en muchas solicitudes válidas, salvo que el receptor admita explícitamente cargas por fragmentos y tu servicio controle el total. De lo contrario, una regla de 10 MB se convierte en una transferencia de 100 partes sin ninguna de las protecciones que creías tener.

Rechaza pronto y devuelve una respuesta que permita al agente recuperarse de forma segura:

```json
{
  "error": "attachment_too_large",
  "purpose": "diagnostic_bundle",
  "observed_bytes": 12582911,
  "max_bytes": 8388608,
  "safe_alternatives": [
    "create_redacted_diagnostic_bundle",
    "attach_selected_log_window"
  ]
}
```

La respuesta importa. Si el rechazo solo dice «la carga ha fallado», el agente puede probar otro destino, comprimir el archivo o seguir reintentando. Indícale qué acción puede realizar a continuación, sin exponer el archivo rechazado mediante un mensaje de depuración.

El almacenamiento en búfer es otro fallo silencioso. Muchos frameworks analizan una solicitud multipart en memoria o en un directorio temporal antes de que el código de la aplicación pueda ver su tamaño. Configura el servidor web, el parser del framework, el proxy inverso y el lector de la aplicación con un límite coherente. El límite más pequeño prevalece, pero una capa inesperadamente más grande todavía puede consumir espacio en disco antes de que la capa menor rechace la solicitud.

Mide el tamaño después del procesamiento para la normalización de texto, la conversión de imágenes, la extracción de documentos y la descompresión de archivos. Un límite que solo cubre el adjunto original no controla el uso de recursos ni el potencial de divulgación del material que generas después.

## El nombre de archivo y el encabezado MIME casi no prueban nada

Las comprobaciones del tipo de archivo necesitan pruebas independientes, porque la extensión y el encabezado `Content-Type` proceden del remitente. Un agente puede transmitir una etiqueta engañosa sin intención maliciosa. Una herramienta de soporte puede llamar a cada adjunto `application/octet-stream`. En ambos casos, el receptor debe decidir basándose en los bytes y en la estructura permitida.

La guía File Upload Cheat Sheet de OWASP recomienda permitir extensiones mediante listas explícitas, no confiar en el encabezado `Content-Type`, generar nombres en el servidor y almacenar las cargas fuera de la raíz web. Esa guía sigue siendo válida, pero los flujos de agentes necesitan una regla adicional: valida el tipo según la finalidad indicada antes de que el servicio contacte con el receptor remoto. Un PDF válido no es automáticamente válido para todas las finalidades de carga.

Usa varias comprobaciones que respondan a preguntas diferentes:

- La extensión indica qué afirma enviar el remitente.
- Los bytes de firma indican si el contenido comienza como el formato declarado.
- Un parser limitado indica si los bytes cumplen suficientes características del formato para tratarlos de forma segura.
- La inspección del contenido indica si el archivo contiene material prohibido para esa finalidad.

Para una exportación CSV, acepta una lista pequeña como `.csv` y texto UTF-8, analiza una muestra limitada y rechaza los datos binarios incrustados o las filas inesperadamente anchas. Para un PDF, verifica la firma `%PDF-`, aplica un límite de tamaño y usa un parser con límites de tiempo y memoria si necesitas inspeccionar las páginas. Para las imágenes, decodifica las dimensiones antes de procesarlas. Una imagen de tamaño moderado puede reservar demasiada memoria al decodificarse.

Evita un tipo genérico «archivo comprimido». ZIP, TAR y GZIP son distintos y cada uno requiere su propio trabajo de inspección. Si un proceso empresarial no necesita un archivo comprimido, recházalo. Aceptar un formato porque a veces lo quieren los usuarios es la forma en que se construyen los endpoints genéricos de adjuntos.

Cambia el nombre de los archivos aceptados en el lado del servicio. Conserva el nombre original como metadato visible después de limpiarlo, pero no lo uses como ruta del sistema de archivos, clave del almacén de objetos ni valor de `Content-Disposition` sin escaparlo. Los nombres pueden contener caracteres de control, Unicode engañoso, separadores de ruta y cadenas que alteren los registros posteriores.

## Las comprobaciones del destino deben sobrevivir a las redirecciones

Una lista permitida de receptores debe identificar el lugar exacto que puede recibir el archivo. Permitir `https://example.com` no basta si el cliente HTTP sigue redirecciones hacia otro host, resuelve una dirección interna o acepta otro puerto.

Almacena los destinos como registros del servidor con esquema, host, puerto, prefijo de ruta, identidad de credenciales y finalidades aceptadas fijos. La acción recibe `destination_id`, nunca un endpoint libre. Si un receptor necesita un ID de caso en la ruta, constrúyelo a partir de un identificador restringido en lugar de aceptar una URL completa del agente.

Para cada envío, el cliente HTTP debe aplicar estas comprobaciones:

1. Exige HTTPS, salvo que exista una excepción interna documentada.
2. Compara el host y el puerto solicitados con el registro del destino antes de conectarse.
3. Desactiva las redirecciones por defecto. Si un receptor las necesita, valida cada destino de redirección contra el mismo registro antes de enviar otro byte.
4. Rechaza literales IP, direcciones de loopback, direcciones link-local y rangos privados, salvo que ese destino exista explícitamente para un servicio interno controlado.
5. Fija el prefijo de ruta y el método HTTP permitidos en lugar de permitir todo un host.

El cuarto punto suele describirse como protección contra SSRF, y lo es. También evita la divulgación accidental a través de un agente que sigue una URL incluida en la descripción de una tarea. Un cliente de carga con acceso a redes internas nunca debe tratar el texto de una tarea como autoridad para contactar con una dirección.

No reenvíes el contexto de autorización original. La credencial usada para enviar a una API de gestión de casos debe pertenecer únicamente a ese receptor y a esa acción. Una puerta de enlace de carga que copie encabezados arbitrarios del agente crea una vía más sencilla para inyectar encabezados y permite que un agente seleccione credenciales indirectamente.

Registra la URL final después de las redirecciones, pero oculta los valores de consulta en los registros operativos. Las cadenas de consulta suelen contener tokens de carga firmados. Registrar el destino es útil; reproducir un token de autorización utilizable es una imprudencia.

## Los registros necesitan una ruta de exportación deliberada

Los registros son la clase de adjunto que más se subestima. Capturan encabezados de solicitudes, identificadores de clientes, fragmentos SQL, trazas de pila, nombres de host internos y, a veces, cuerpos completos de solicitudes o respuestas. Que un registro se encuentre en el directorio de un desarrollador no significa que sea seguro enviarlo por correo o cargarlo.

No resuelvas esto con una instrucción que diga al agente «elimina los secretos». Los agentes pueden pasar por alto formatos, ocultar demasiado o decidir que una cadena sospechosa es un contexto inofensivo. Crea un generador de paquetes de diagnóstico que lea archivos conocidos, aplique filtros deterministas y produzca un artefacto nuevo en un directorio de preparación.

Una política práctica puede eliminar campos completos en lugar de buscar todos los patrones posibles de secretos. Elimina por defecto `Authorization`, `Cookie`, `Set-Cookie`, los campos de claves de API, los identificadores de sesión y los cuerpos de las solicitudes. Sustituye los identificadores de clientes por tokens locales estables cuando sea necesario correlacionarlos. Limita las marcas de tiempo a la ventana del incidente en lugar de enviar semanas de historial.

Por ejemplo, esta es una estructura más segura para un registro de diagnóstico que una traza HTTP sin procesar:

```json
{
  "time": "2025-03-08T14:22:11Z",
  "request_id": "local-7f3c",
  "method": "POST",
  "route": "/v1/reports",
  "status": 502,
  "upstream": "reporting-service",
  "authorization": "[removed]",
  "body": "[omitted]"
}
```

El generador del paquete debe producir un manifiesto con nombres de archivo, recuentos de bytes, hashes y filtros aplicados. Así el revisor tiene algo concreto que inspeccionar sin abrir cada adjunto. También permite al receptor identificar una carga truncada o modificada.

Las exportaciones de bases de datos necesitan una regla más estricta. Un agente no debe adjuntar una exportación de producción solo porque un ticket diga «envía una muestra». Genera un artefacto que contenga únicamente el esquema, una consulta específica con columnas aprobadas o una reproducción sintética. Si un incidente requiere registros reales de clientes, conviértelo en una acción independiente con destino, alcance y aprobación humana explícitos.

Las capturas de pantalla merecen la misma cautela. Pueden incluir cuentas de clientes, mensajes, pestañas del navegador, notificaciones y rutas locales. Recorta la imagen o genera una captura específica mediante una herramienta controlada en lugar de entregar al agente un directorio amplio de capturas.

## Los archivos comprimidos y los documentos de oficina ocultan más de un archivo

Un archivo comprimido crea un segundo conjunto de cargas dentro del primero. Antes de enviarlo, inspecciona su lista de miembros y aplica límites al número de miembros, los bytes comprimidos, los bytes expandidos, la profundidad de las rutas y el anidamiento. Rechaza las entradas que usen rutas absolutas, traversal con `..`, nombres duplicados o enlaces simbólicos.

Un archivo ZIP de 2 MB en disco puede expandirse hasta agotar un worker o un receptor. Esto suele llamarse bomba de descompresión, pero el fallo no se limita a entradas maliciosas. Los sistemas de compilación pueden producir archivos comprimidos enormes por error, y un agente puede adjuntar el primer archivo que parezca una exportación.

Los documentos de oficina requieren una cautela similar. Los formatos de documentos modernos suelen contener contenedores ZIP, medios incrustados, metadatos, comentarios, cambios registrados y relaciones externas. Un documento puede parecer limpio en la página y conservar texto anterior o información del autor en su paquete. Si el flujo solo necesita el contenido renderizado, produce un PDF nuevo a partir de datos aprobados en lugar de reenviar el original editable.

No intentes «sanear» recursivamente documentos arbitrarios dentro de la ruta de solicitud. Analizar y reescribir formatos complejos tiene sus propios costes de seguridad y fiabilidad. Para un flujo limitado, acepta un conjunto limitado de artefactos generados. Para un documento excepcional, envíalo a un proceso de revisión humana que pueda inspeccionar el archivo real y su destino.

## El contrato de la acción debe hacer imposibles las solicitudes inseguras

Una herramienta orientada a agentes debe ofrecer opciones que coincidan con tus controles, no un generador de solicitudes HTTP sin restricciones. Si la herramienta ofrece `url`, `headers`, `file_path` y `method`, la política ya ha perdido casi toda su forma.

Usa un esquema de solicitud en el que los campos no confiables describan la intención y los registros confiables aporten la autoridad. Este ejemplo mantiene fuera del control del agente el receptor, las credenciales y la clase de archivo permitida:

```json
{
  "action": "send_attachment",
  "purpose": "customer_export",
  "destination_id": "finance-import",
  "artifact_id": "exp_8c4e1a",
  "note": "March reconciliation correction"
}
```

El servicio resuelve `artifact_id` como un objeto preparado que creó o aceptó mediante un flujo de entrada independiente. Calcula por sí mismo el resumen criptográfico y el tipo detectado. Resuelve `destination_id` como un registro de receptor fijo. Añade la credencial del receptor en el momento del envío. El agente nunca ve esa credencial ni puede sustituir el receptor después de la aprobación.

El resultado de la comprobación previa debe mostrar suficiente información para tomar una decisión informada:

```json
{
  "decision": "approval_required",
  "artifact": {
    "name": "reconciliation-2025-03.csv",
    "bytes": 482913,
    "detected_type": "text/csv",
    "sha256": "a4d1...c09e"
  },
  "destination": {
    "label": "Finance import",
    "host": "imports.example.internal",
    "path": "/v2/reconciliation"
  },
  "reason": "customer_export requires approval"
}
```

No muestres al revisor únicamente el nombre del archivo y un botón de aprobación. Muestra el tamaño medido, el tipo detectado, el host del destino, la etiqueta del destino y la finalidad. Si el archivo es sensible, muestra un resultado de clasificación de muestra o un manifiesto, no todo su contenido en la pantalla de aprobación.

Haz que los IDs de artefacto caduquen pronto y tengan una sola finalidad. Un paquete de diagnóstico preparado no debe seguir siendo reutilizable como objeto de carga genérico después de cerrar el caso de soporte. Vincúlalo a la finalidad y al receptor cuando lo crees y haz que caduque tras una ventana operativa breve.

Los reintentos necesitan idempotencia. Los fallos de red son habituales y los agentes reintentan de forma agresiva. Genera un token de idempotencia en la capa de acción, vincúlalo al resumen del artefacto y al destino, y reutilízalo únicamente para el mismo envío previsto. No permitas que un reintento con una ruta o un resumen distintos herede la autorización anterior.

## La aprobación humana funciona cuando marca un límite real

La aprobación no sustituye a la validación. Una persona no puede identificar de forma fiable una bomba ZIP, un archivo disfrazado o un fallo de redirección desde una ventana emergente. La aprobación sirve para la decisión que la política no puede tomar automáticamente: si una exportación concreta de clientes debe ir a un receptor concreto para ese incidente.

Usa aprobación por llamada para envíos de alta sensibilidad, receptores nuevos, exportaciones de producción y artefactos de diagnóstico amplios. Permite que los artefactos rutinarios y limitados se envíen bajo una finalidad aprobada si el destino y el contenido están restringidos. Pedir a una persona que apruebe cada informe de prueba hace que apruebe sin leer.

El registro de aprobación debe estar vinculado al resumen del artefacto, la finalidad declarada y el registro del destino. Si cualquiera de ellos cambia, descarta la aprobación. Un nombre de archivo no basta, porque dos archivos pueden compartir nombre y contener bytes distintos.

Sallyport puede mantener al agente alejado de la credencial usada para una carga HTTP, y su opción de aprobación por clave encaja con los envíos que necesitan una decisión humana en cada ocasión. Eso no elimina la necesidad de un contrato de artefactos a nivel de aplicación, porque la puerta de enlace no puede inferir si un CSV de clientes pertenece a un caso de soporte.

Conserva dos vistas de auditoría: una que indique qué ejecución del agente recibió autoridad para actuar y otra que registre cada intento y resultado de transferencia. Un registro de auditoría debe incluir el resumen criptográfico, los bytes medidos, el veredicto del tipo, la finalidad, el registro del receptor, la decisión, el estado de la respuesta y la hora. Guarda los secretos y el contenido completo de los archivos en otro lugar, si es que decides conservarlos.

## Prueba las rutas de rechazo antes de que las encuentre un agente

Una política que solo funciona en el caso ideal está incompleta. Construye un receptor aislado que registre la solicitud exacta que recibió y usa dispositivos de prueba que hagan saltar cada límite.

Empieza con una solicitud de carga sencilla contra tu receptor de pruebas:

```bash
curl -i -X POST https://receiver.test/attachments \\
  -H 'Authorization: Bearer test-token' \\
  -F 'file=@fixtures/diagnostic.zip;type=application/zip' \\
  -F 'case_reference=CASE-1842'
```

Una prueba correcta debe confirmar el método, el host final, la ruta, el resumen y el tamaño. Las pruebas útiles son las que demuestran que el receptor no recibió nada. Comprueba que un cuerpo demasiado grande se rechaza antes de reenviarlo, que un encabezado `text/plain` falsificado no puede hacer pasar una carga binaria y que un destino no incluido en la lista nunca recibe una conexión.

Incluye estos casos en tu conjunto de pruebas:

- Una extensión válida con bytes de firma que no coinciden.
- Un archivo comprimido cuyo tamaño expandido supera la regla.
- Una redirección desde un host permitido hacia otro no permitido.
- Un registro de prueba que contenga un encabezado de autorización y una dirección de correo de cliente.
- Un reintento que use el mismo token de idempotencia con bytes de archivo distintos.

Inspecciona los registros del servidor durante estas pruebas. Debes comprobar dos cosas: que la transferencia no se produjo y que tus propios registros no conservaron el cuerpo rechazado, el token bearer ni la URL firmada. Los equipos suelen arreglar la ruta de red y dejar el mismo material sensible en las trazas de excepciones.

Ejecuta las pruebas mediante la misma interfaz de herramienta del agente que usan los agentes de producción. Un cliente HTTP interno seguro no sirve de nada si el wrapper orientado al agente puede seleccionar una URL arbitraria o saltarse la ruta de preparación de artefactos.

## Haz que la ruta segura sea más fácil que la ruta sin restricciones

Los equipos evitan los controles de carga cuando la ruta aprobada es lenta, ambigua o no puede gestionar el trabajo habitual de soporte. Crea un conjunto pequeño de artefactos que la gente realmente necesite: un paquete de diagnóstico con datos ocultos, una exportación limitada, un informe generado y una captura de incidente con límites definidos. Haz que cada uno sea fácil de solicitar por su nombre.

Mantén el cargador genérico sin restricciones fuera de la lista de herramientas del agente. Si un ingeniero lo necesita en un caso poco habitual, exige un proceso operativo interactivo en el que elija el archivo y el receptor con todo el contexto. Esa incomodidad es adecuada porque la acción no tiene una clasificación automatizada fiable.

Revisa los envíos rechazados con la misma seriedad que los correctos. Un patrón de paquetes demasiado grandes indica que el artefacto de diagnóstico no está bien diseñado. Los intentos repetidos de enviar registros a un host nuevo pueden significar que la lista de destinos necesita una incorporación justificada, o que un prompt del agente intenta rodear tus controles. La diferencia solo aparece en el registro si capturas juntos la finalidad, el veredicto del archivo y la decisión del receptor.

El primer cambio que haría es sencillo: elimina las URL libres y las rutas de archivo arbitrarias de la acción de carga del agente. Cuando la acción solo acepta artefactos preparados, finalidades con nombre e IDs de destino, los límites de tamaño y las comprobaciones de contenido tienen un lugar fiable donde operar.
