🔐Ciberseguridad

Agentes de IA y secretos: cómo evitar exponer credenciales durante el desarrollo

Felipe••16 min de lectura
Protege credenciales al trabajar con agentes de IA: permisos mínimos, aislamiento, gestores de secretos y un laboratorio local con Node.js y Docker.

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:

SuperficieQué podría contenerCómo podría incorporarse a la tarea
.env, archivos de configuración o certificadosAPI keys, contraseñas, claves privadasLectura de archivos o búsqueda de texto
Configuración de CLI y gestores de paquetesTokens de registro, sesiones cloudComandos de diagnóstico o autenticación
Logs, errores y trazasCabeceras, cookies, cadenas de conexiónAnálisis de una prueba fallida
Historial Git y cambios pendientesCredenciales eliminadas del archivo actualRevisión de commits o diferencias
Variables del procesoSecretos inyectados al iniciarEjecución de scripts y procesos hijos
Herramientas externasDatos privados y respuestas autenticadasConsultas 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.example

En .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:

TareaAcceso razonable como punto de partidaAcceso que requiere justificación adicional
Explicar una funciónCódigo seleccionado y documentaciónConfiguración privada o sesiones cloud
Ejecutar pruebas unitariasCódigo, dependencias revisadas y datos ficticiosCredenciales de producción
Consultar una integración de pruebaOperación de lectura y cuenta de pruebas limitadaAdministración del servicio
Preparar un cambio de despliegueConfiguración revisableEjecutar 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_TOKEN

Crea .env con este contenido:

DEMO_API_TOKEN=FICTICIO_NO_VALIDO

El 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.mjs

Salida 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.mjs

Salida 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.mjs

Salida 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ónValor en el entornoValor en .env
Node con el archivo cargadoSíSí
Proceso hijo con entorno explícitoNoSí
Contenedor con solo el script montadoNoNo

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-lab

La 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:

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:

#agentes de IA#gestión de secretos#credenciales#Node.js#TypeScript#Infisical#Azure Key Vault#mínimo privilegio

Artículos relacionados