Le pides a un agente que investigue por qué falla una prueba. Para encontrar la causa, abre archivos, ejecuta comandos y consulta errores. En ese recorrido puede encontrarse con un .env, una cabecera de autorización dentro de un log o un token que quedó guardado en un commit antiguo.
El problema no exige una intención maliciosa. Una tarea legítima puede terminar copiando una credencial a la salida de una herramienta y, desde allí, al contexto de un modelo o a un servicio externo.
Ya vimos por qué las variables de entorno no sustituyen a un gestor de secretos. Aquí cambia la pregunta: ¿qué puede leer, ejecutar y transmitir un agente mientras trabaja en nuestro proyecto? Vamos a separar esas capacidades y comprobar algunas de sus diferencias con un laboratorio local que solo utiliza un valor ficticio.
Las medidas siguientes son recomendaciones y decisiones de diseño según el riesgo. Utilizar una guía de OWASP no demuestra por sí solo cumplimiento normativo.
Del archivo local al contexto del agente
Un agente de desarrollo combina un modelo con herramientas. El modelo puede proponer una lectura o un comando; la aplicación que lo ejecuta determina los permisos y qué resultados vuelven al contexto. No todas las herramientas funcionan igual: hay que revisar el producto, su configuración y las integraciones habilitadas.
Para una revisión inicial, este inventario ayuda a identificar dónde puede aparecer información sensible:
| Superficie | Qué podría contener | Cómo podría incorporarse a la tarea |
|---|---|---|
.env, archivos de configuración o certificados | API keys, contraseñas, claves privadas | Lectura de archivos o búsqueda de texto |
| Configuración de CLI y gestores de paquetes | Tokens de registro, sesiones cloud | Comandos de diagnóstico o autenticación |
| Logs, errores y trazas | Cabeceras, cookies, cadenas de conexión | Análisis de una prueba fallida |
| Historial Git y cambios pendientes | Credenciales eliminadas del archivo actual | Revisión de commits o diferencias |
| Variables del proceso | Secretos inyectados al iniciar | Ejecución de scripts y procesos hijos |
| Herramientas externas | Datos privados y respuestas autenticadas | Consultas a APIs o servidores MCP |
Es un mapa de posibilidades, no una afirmación de que cualquier agente acceda automáticamente a todo ello. OWASP identifica precisamente el runtime, el contexto del repositorio y las herramientas conectadas como fronteras que deben revisarse en el desarrollo asistido por IA.
Conviene distinguir que un secreto exista en el equipo, que el proceso del agente pueda leerlo y que se envíe al modelo. Son pasos diferentes. Evitar el primero en el espacio de trabajo reduce la dependencia de filtros en los pasos siguientes.
Un .env ignorado por Git sigue siendo un archivo legible
Una configuración habitual para un proyecto Node.js es conservar una plantilla sin credenciales y excluir los archivos locales:
.env
.env.*
!.env.exampleEn .env.example puedes documentar nombres y valores no sensibles, dejando vacíos los secretos:
NODE_ENV=development
PORT=3000
PAYMENTS_API_TOKEN=Esto ayuda a evitar incorporaciones accidentales al repositorio, pero .gitignore no impone permisos de lectura. Tampoco deja de seguir un archivo que ya estaba versionado. Un agente con acceso al sistema de archivos puede encontrar un .env aunque Git lo ignore. Documentación de gitignore.
Si construyes imágenes, revisa también .dockerignore: excluye secretos del contexto enviado al constructor. Su función es distinta y no limita los archivos que montes después en un contenedor. Docker: contexto de construcción y archivos de exclusión.
Para revisar código, suele bastar una plantilla, datos de prueba y una explicación del contrato de configuración. Si la tarea necesita acceder a un entorno real, concede ese acceso de forma separada y acotada; no copies todo el entorno del desarrollador por comodidad.
Lectura y escritura protegen cosas diferentes
Un montaje de solo lectura impide modificar los archivos montados, pero permite consultar su contenido. Para la confidencialidad, leer una clave puede ser suficiente para comprometerla.
También hay que precisar sobre qué recurso aplica cada permiso. Un agente podría tener el repositorio en modo lectura y conservar una terminal con acceso a la red. O disponer de una API de consulta que devuelve información privada. Ninguno de esos escenarios equivale a «no puede causar efectos externos».
Mi criterio de diseño es asignar capacidades por tarea:
| Tarea | Acceso razonable como punto de partida | Acceso que requiere justificación adicional |
|---|---|---|
| Explicar una función | Código seleccionado y documentación | Configuración privada o sesiones cloud |
| Ejecutar pruebas unitarias | Código, dependencias revisadas y datos ficticios | Credenciales de producción |
| Consultar una integración de prueba | Operación de lectura y cuenta de pruebas limitada | Administración del servicio |
| Preparar un cambio de despliegue | Configuración revisable | Ejecutar el despliegue y leer sus secretos |
La tabla es una propuesta para organizar permisos, no una regla universal. El principio de mínimo privilegio aplicado a agentes implica restringir herramientas y verificar la autoridad en el componente que ejecuta cada operación.
La terminal también es una vía de salida
Un secreto puede entrar en el contexto sin que el agente abra directamente su archivo. Basta con que un script imprima el objeto de configuración, un cliente HTTP incluya cabeceras en su modo de depuración o un error serialice una URL con contraseña.
Por eso conviene evitar volcados completos de entorno, configuración y respuestas de SDK. Los parámetros de comandos también pueden quedar en historiales o registros. Cuando una integración necesite una credencial, utiliza el mecanismo de entrega previsto y revisa qué conserva o imprime.
OWASP recomienda excluir o tratar especialmente tokens, contraseñas, claves y cadenas de conexión en los logs. Logging Cheat Sheet: datos que deben excluirse.
En TypeScript, una salida pequeña puede construirse con campos definidos por el programa en lugar de serializar un objeto de error completo:
function recordTestResult(passed: boolean, durationMs: number): void {
const safeDuration = Number.isFinite(durationMs) && durationMs >= 0
? Math.round(durationMs)
: 0;
console.log(JSON.stringify({
event: 'agent.tests.completed',
outcome: passed === true ? 'passed' : 'failed',
durationMs: safeDuration,
}));
}
recordTestResult(true, 125);El ejemplo registra un resultado, no demuestra que unas pruebas reales hayan tardado 125 ms. No recibe credenciales ni copia propiedades adicionales. En una aplicación, esos valores deben proceder de la ejecución observada.
Un filtro de redacción posterior puede ser una defensa adicional, pero quizá no reconozca una credencial transformada o incluida en un texto libre. Es preferible no producir el dato sensible que intentar borrarlo después de que haya recorrido varias herramientas.
Git puede conservar lo que ya no ves en el archivo
Eliminar una API key del último estado no la elimina de commits anteriores, clones, forks o copias de una revisión. Un agente que investiga una regresión puede leer ese historial y volver a mostrarla.
Si una credencial real estuvo expuesta, la primera respuesta es revocarla o rotarla en el sistema que la acepta. Después se evalúa la limpieza del historial y de otras copias, coordinando sus efectos con el equipo. Reescribir commits no invalida por sí solo una clave. GitHub: retirar datos sensibles de un repositorio.
Para el trabajo del agente, revisa también qué historial necesita. Si le entregas una copia reducida del código para una tarea puntual, evita incluir .git sin una razón concreta. Cuando el historial sí sea necesario, trátalo como otra fuente potencialmente sensible; no lo vuelques completo al contexto para «dar más información».
Envío involuntario y controles de ejecución
La exposición puede producirse al enviar contexto al proveedor del modelo, llamar a una herramienta remota o copiar diagnósticos a un servicio de soporte. Debes conocer destinos, retención, acceso y condiciones de tratamiento; «no usar datos para entrenamiento» no significa automáticamente «no almacenar datos».
Además, contenido de repositorios o herramientas puede intentar inducir prompt injection: por ejemplo, pedir al agente que adjunte configuración privada para completar una supuesta comprobación. No hace falta seguir esa petición para analizar el documento que la contiene.
Un entorno aislado debería limitar archivos, identidades, procesos y destinos de red según la tarea. Un clon en otra carpeta o un worktree ayuda a separar cambios, pero no crea por sí solo una frontera de permisos. Para tareas sin red, bloquéala desde el runtime; si es necesaria, autoriza destinos y operaciones concretos. OWASP propone estos controles en su guía de seguridad de agentes y MCP.
Permitir un dominio tampoco autoriza cualquier contenido enviado a él. Una API legítima puede aceptar mensajes o archivos que contengan datos que no deberían salir. Los controles necesitan considerar destino, operación y datos, no solo la URL.
Laboratorio local: tres fronteras distintas
Usaremos Node.js 22 o superior, Docker con contenedores Linux y una terminal compatible con Bash. No necesitas instalar paquetes ni conectar un modelo. El proceso de prueba representa una herramienta local que un agente podría ejecutar; no simula decisiones de IA ni demuestra una filtración a un proveedor.
El único valor utilizado será FICTICIO_NO_VALIDO. No sustituye ni debe sustituirse por una credencial real.
1. Preparar los archivos de prueba
Trabaja fuera de tus proyectos, en una carpeta nueva:
mkdir agentes-secretos-lab && cd agentes-secretos-lab
unset DEMO_API_TOKENCrea .env con este contenido:
DEMO_API_TOKEN=FICTICIO_NO_VALIDOEl unset evita que una variable previa del mismo nombre tenga precedencia sobre el archivo. Node.js permite cargarlo explícitamente con --env-file.
Guarda lo siguiente en probe.mjs:
import { readFileSync } from 'node:fs';
const expected = 'FICTICIO_NO_VALIDO';
let tokenEnArchivo = false;
try {
const content = readFileSync('.env', 'utf8');
tokenEnArchivo = content.includes(`DEMO_API_TOKEN=${expected}`);
} catch (error) {
if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
throw error;
}
}
console.log(JSON.stringify({
tokenEnEntorno: process.env.DEMO_API_TOKEN === expected,
tokenEnArchivo,
}));El script comprueba si alcanza nuestro valor por dos vías. Solo imprime booleanos; no enumera variables ni muestra contenido del equipo.
Ejecuta:
node --env-file=.env probe.mjsSalida observada:
{"tokenEnEntorno":true,"tokenEnArchivo":true}La herramienta puede obtener el valor tanto del entorno como del archivo. Si devolviera esos contenidos en un diagnóstico, podrían quedar disponibles para quien reciba la salida.
2. Evitar la herencia del entorno no oculta archivos
Los procesos hijos de Node.js heredan process.env de forma predeterminada. Podemos construir un entorno explícito. Guarda este archivo como run-probe.mjs:
import { execFileSync } from 'node:child_process';
const output = execFileSync(process.execPath, ['probe.mjs'], {
cwd: import.meta.dirname,
env: { NODE_ENV: 'test' },
shell: false,
encoding: 'utf8',
timeout: 5_000,
maxBuffer: 4_096,
});
process.stdout.write(output);node --env-file=.env run-probe.mjsSalida observada:
{"tokenEnEntorno":false,"tokenEnArchivo":true}El padre recibe la variable, pero no la transmite al hijo. Sin embargo, ambos siguen teniendo acceso al directorio del laboratorio. Filtrar el entorno no es aislar el sistema de archivos.
El ejecutable y los argumentos están fijados; no se utiliza una cadena de shell generada por el modelo. El timeout y el límite de salida son medidas operativas, no una barrera completa frente a código hostil. execFileSync bloquea el proceso padre y se usa aquí por simplicidad, no como diseño de un servidor concurrente. API de procesos hijos de Node.js.
Reenviamos la salida porque conocemos el script y solo devuelve dos booleanos. Para una herramienta real, revisa también stdout, stderr y los errores antes de entregarlos al agente.
3. Ejecutar solo el código necesario
Ahora montaremos únicamente probe.mjs en un contenedor, sin .env ni variables sensibles. Para repetir la prueba con el artefacto utilizado, emplea esta imagen identificada por digest:
LAB_IMAGE='node@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14'
docker pull "$LAB_IMAGE"
docker run --rm \
--network none \
--read-only \
--cap-drop ALL \
--security-opt no-new-privileges=true \
--user 1000:1000 \
--pids-limit 64 \
--memory 128m \
--cpus 1 \
--mount "type=bind,source=$PWD/probe.mjs,target=/lab/probe.mjs,readonly" \
--workdir /lab \
"$LAB_IMAGE" node probe.mjsSalida observada:
{"tokenEnEntorno":false,"tokenEnArchivo":false}La descarga de la imagen usa la red del host. --network none se aplica al contenedor que ejecuta la prueba y deja únicamente su interfaz de loopback. No bloquea las comunicaciones de otras aplicaciones ni las del cliente de un agente ejecutado fuera del contenedor. Docker: controlador de red none.
El montaje de solo lectura protege el script frente a cambios desde el contenedor. La ausencia del montaje de .env es lo que evita leer ese archivo del host por esa vía. El usuario sin privilegios, la eliminación de capacidades y los límites de recursos añaden restricciones; no convierten el contenedor en una garantía absoluta. Docker: ejecución de contenedores.
No montes el socket de Docker, el directorio personal ni configuraciones cloud dentro de un entorno destinado a código no confiable. Si el agente puede cambiar estas restricciones desde fuera, también puede deshacer la separación que acabamos de establecer.
Qué demostramos y cómo limpiar
Los tres casos se ejecutaron y produjeron las salidas mostradas. No prueban que cualquier archivo esté inaccesible ni que un contenedor resista todos los ataques. Comprueban estas rutas concretas hacia un valor ficticio:
| Ejecución | Valor en el entorno | Valor en .env |
|---|---|---|
| Node con el archivo cargado | Sí | Sí |
| Proceso hijo con entorno explícito | No | Sí |
| Contenedor con solo el script montado | No | No |
Los contenedores se eliminan con --rm. Desde la carpeta del laboratorio, puedes retirar los archivos creados:
rm .env probe.mjs run-probe.mjs
cd ..
rmdir agentes-secretos-labLa imagen descargada permanece en la caché de Docker y puede reutilizarse. Fijar su digest facilita reproducir la prueba; para otros usos, revisa sus actualizaciones y su estado de seguridad.
Gestores de secretos: separar acceso de entrega de credenciales
Un gestor puede autenticar consumidores, aplicar permisos y administrar el ciclo de vida de secretos. Pero hay dos diseños muy diferentes: entregar la credencial al runtime controlado por el agente o mantenerla en un componente confiable que ejecuta una operación limitada.
Para tareas que no necesitan ver el valor, esta segunda opción reduce la exposición:
No se pudo mostrar el diagrama. Puedes consultar su definición.
sequenceDiagram
participant A as Agente de desarrollo
participant E as Ejecutor confiable
participant G as Gestor de secretos
participant S as Servicio de pruebas
A->>E: Solicitar operación permitida
Note over E: Verificar identidad, recurso y alcance
E->>G: Obtener credencial con identidad propia
G-->>E: Credencial limitada
E->>S: Ejecutar operación autenticada
S-->>E: Resultado
E-->>A: Campos permitidos, sin credencial
Es una propuesta arquitectónica, no una función automática de cualquier gestor. El ejecutor no debería aceptar destinos, comandos o nombres de secretos arbitrarios. Debe validar parámetros y devolver únicamente los datos necesarios. Una función llamada readSecret sigue exponiendo secretos aunque esté servida mediante MCP.
Infisical
Las identidades de máquina permiten autenticar aplicaciones y cargas de trabajo con roles y métodos de autenticación propios. En este diseño, la identidad pertenece al ejecutor y se limita al proyecto y a los secretos que necesita, en lugar de reutilizar la sesión personal del desarrollador. Infisical: machine identities.
Los secretos dinámicos pueden generar credenciales bajo demanda y gestionar su revocación en servicios compatibles. Verifica el proveedor y el plan: la documentación consultada sitúa esta capacidad en Advanced. No toda clave estática guardada en Infisical se convierte en temporal. Infisical: dynamic secrets.
El tutorial de Infisical con Node.js y TypeScript desarrolla la integración. Aquí importa quién recibe el valor: si un comando lo exporta o imprime dentro del entorno accesible al agente, la centralización no evita esa exposición.
Azure Key Vault
En Azure, una carga de trabajo compatible puede usar una identidad administrada para autenticarse, evitando distribuir una credencial permanente de acceso a Key Vault. Los permisos de datos siguen siendo necesarios y deben limitarse. Microsoft recomienda RBAC, separación por aplicación y entorno, y restricciones de red según el escenario. Seguridad de Azure Key Vault.
La identidad administrada no impide que código autorizado recupere un secreto. Si el agente controla ese código o puede ejecutar comandos bajo la misma identidad, hay que considerar también esa capacidad. El almacén protege la entrega según su política; no puede garantizar qué hará cada consumidor después de recibir el valor.
Credenciales temporales y ejecución de código modificado
Limita la duración y el alcance a la tarea: entorno de pruebas, recursos concretos y operaciones necesarias. Distingue expiración, que limita vigencia; revocación, que retira acceso; y rotación, que reemplaza una credencial. Su efecto y propagación dependen del sistema que valida el acceso. OWASP: ciclo de vida de secretos.
Hay un límite importante: un token temporal del gestor puede permitir leer una contraseña duradera. Que expire el primero no invalida la segunda si alguien ya la copió. La evaluación debe seguir hasta la credencial final, no detenerse en la autenticación del gestor.
Tampoco basta con ocultar secretos durante la edición y entregarlos automáticamente al ejecutar el código recién modificado. Un cambio podría añadir un envío de datos que se active en ese momento. El paso de código propuesto a código ejecutado con credenciales necesita una revisión independiente del agente que lo escribió.
En CI/CD, separa pruebas sin secretos de trabajos privilegiados y protege la configuración de esos trabajos. La aprobación debe referirse a una versión concreta del código y de la operación. Una autorización anterior no debería habilitar modificaciones posteriores sin evaluación.
Revisar acciones sin crear otro almacén de secretos
La auditoría debe permitir reconstruir qué se autorizó y qué ocurrió: identidad, tarea, herramienta, recurso, decisión de permisos, aprobación cuando corresponda y resultado. Los registros deben proceder de los componentes que ejecutan las operaciones; el resumen narrativo del agente no sustituye esa evidencia. OWASP: monitorización de agentes.
En la revisión posterior, comprobaría el diff completo y los cambios en scripts, dependencias, reglas del agente y pipelines. También revisaría accesos del gestor y operaciones del servicio de destino, correlacionándolos mediante identificadores que no contengan credenciales.
Guardar todos los argumentos y todas las respuestas sin selección puede reproducir la filtración dentro del sistema de auditoría. Limita contenido, acceso y retención. Si aparece una credencial real, detén su difusión, revócala según el riesgo y revisa las copias y accesos; no sigas pegándola para preguntar si «parece un secreto».
Diseñar tareas que necesiten menos autoridad
La mejora más útil suele comenzar antes de ejecutar un comando: preparar datos ficticios, separar los entornos y decidir qué capacidad necesita realmente la tarea. Un agente puede modificar pruebas de una integración sin recibir su token de producción.
Los gestores, el aislamiento y las aprobaciones cumplen funciones diferentes. La arquitectura es más sólida cuando el agente puede avanzar con acceso limitado y una equivocación no convierte automáticamente la terminal, los logs o el contexto en una copia de nuestras credenciales.
Referencias
Documentación oficial consultada el 4 de noviembre de 2026:
- OWASP: Secure Coding with AI.
- OWASP: AI Agent Security.
- OWASP: AI Agent and MCP Security.
- OWASP: Secrets Management.
- OWASP: Logging.
- Git: gitignore y GitHub: retirar información sensible.
- Node.js: procesos hijos y carga de archivos de entorno.
- Docker: ejecución de contenedores, red none y contexto de construcción.
- Infisical: identidades de máquina y secretos dinámicos.
- Microsoft: seguridad de Azure Key Vault.
