TuBrief
구독 채널
비디오
커뮤니티

Por qué no debes dar claves maestras a un agente de Node.js y cómo implementar tokens temporales de 60 segundos

TuBrief 편집팀
2026년 7월 23일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Español한국어Englishहिन्दीFrançaisDeutschPortuguês日本語РусскийBahasa Indonesia中文العربية

관련 영상

Ship 26 NYC - No le des la llave maestra a tu agente17:22

Ship 26 NYC - No le des la llave maestra a tu agente

Vercel

커뮤니티의 다른 글

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

2026년 9월 13일

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

2026년 9월 13일

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

2026년 9월 13일

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

2026년 9월 13일

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Por qué no debes dar claves maestras a un agente de Node.js y cómo implementar tokens temporales de 60 segundos

Al crear agentes autónomos con LangChain o LlamaIndex, tarde o temprano llega el momento de conectarlos a bases de datos y API externas. Es aquí donde suelen ocurrir los accidentes. Con un solo prompt injection, la clave maestra de OpenAI o la contraseña de administración de la base de datos almacenadas en .env quedan expuestas. No importa qué tan minuciosos sean tus prompt guardrails, no servirán de nada. Mientras la capa de razonamiento y la capa de ejecución del LLM estén acopladas, una sola frase basta para vulnerar la seguridad.

Según el informe SAIF (Secure AI Framework) de Google de 2025, el 88% de las empresas que adoptaron agentes experimentaron intentos de prompt injection. La tasa de bloqueo de las técnicas tradicionales basadas en detección de patrones de texto fue de tan solo el 23%. Es más tranquilizador asumir que el propio proceso del agente no es de confianza. En lugar de otorgar permisos al agente, la estructura debe cambiarse para inyectar tokens temporales con una vida útil de solo 60 segundos en la capa de middleware.

Inyección de tokens dinámicos de Vault de 60 segundos en Express

Al utilizar la autenticación AppRole de HashiCorp Vault, se puede emitir un token válido por 60 segundos justo en el momento exacto en que se invoca una herramienta. Cuando el agente realiza una llamada a una API externa, un interceptor interviene en el medio e inserta el token de corta duración (short-lived) en el encabezado.

`typescript
import { Request, Response, NextFunction } from 'express';
import vault from 'node-vault';

interface VaultAppRoleAuth {
roleId: string;
secretId: string;
}

export class EphemeralTokenInjector {
private vaultClient: any;
private roleId: string;
private secretId: string;

constructor(endpoint: string, auth: VaultAppRoleAuth) {
this.vaultClient = vault({ endpoint });
this.roleId = auth.roleId;
this.secretId = auth.secretId;
}

private async getAppRoleToken(): Promise {
const result = await this.vaultClient.approleLogin({
role_id: this.roleId,
secret_id: this.secretId,
});
return result.auth.client_token;
}

public createToolInterceptor(targetServiceRole: string) {
return async (req: Request, res: Response, next: NextFunction) => {
let clientToken: string | null = null;
try {
clientToken = await this.getAppRoleToken();

    const dynamicSecret = await this.vaultClient.write(
      `sys/leases/generate/${targetServiceRole}`,
      { ttl: '60s' }
    );

    req.headers['authorization'] = `Bearer ${dynamicSecret.data.token}`;
    req.body.ephemeralContext = {
      leaseId: dynamicSecret.lease_id,
      expiresAt: Date.now() + 60000,
    };

    next();
  } catch (error) {
    res.status(500).json({ error: 'Failed to inject ephemeral dynamic secret' });
  } finally {
    clientToken = null;
  }
};

}
}

`

Escribir el código no es suficiente. Dado que el recolector de basura (garbage collector) del motor V8 de Node.js se ejecuta a su propio criterio, la cadena de texto del token permanece en la memoria heap durante un tiempo incluso después de vaciar la variable. Si sufres un ataque de heap dump, existe riesgo de filtración.

  1. Inmediatamente al recibir la respuesta de la API, guarda los valores sensibles en un objeto Buffer.
  2. Una vez completada la ejecución, ejecuta Buffer.fill(0) para purgar los bytes a 0 de forma forzada.
  3. Asigna null a la variable de referencia para entregarla como alimento al GC.

Siguiendo únicamente estos tres pasos, la probabilidad de que un token sea robado mediante perfilado de memoria se reduce drásticamente.

Método de gestión de credenciales Tiempo de vida promedio (TTL) Alcance del daño en caso de robo Trazabilidad de auditoría
Master API Key codificada de forma rígida Ilimitado Control total de toda la infraestructura Imposible identificar al sujeto por compartir una sola clave
Static Secret en variable de entorno (.env) Duración del proceso Abuso total de los permisos de ese proceso de Node Imposible rastrear flujos de trabajo por usuario
Token efímero AppRole de Vault 60 segundos Limitado a 1 sola ejecución de esa Tool Rastreo al segundo mediante Vault Audit Log

Transferencia de Auth0 OIDC Claims a políticas RLS de PostgreSQL

Aunque el agente ejecute una orden como "consulta la información de todos los usuarios" debido a un jailbreak en el prompt, basta con que el motor de la base de datos lo rechace. Si te encuentras en un entorno SaaS multitenant, debes configurar la Seguridad a Nivel de Fila (Row Level Security o RLS) de PostgreSQL.

Extrae el userId y tenantId del JWT emitido por Auth0 y pásalos al contexto de sesión RunnableConfig de LangChain. Este contexto se inyecta como variables de sesión dentro de la transacción de Prisma.

`typescript
import { RunnableConfig } from '@langchain/core/runnables';
import { PrismaClient } from '@prisma/client';

export interface AgentUserClaims {
userId: string;
tenantId: string;
}

export async function executeAgentToolWithRLS(
prisma: PrismaClient,
config: RunnableConfig,
dbOperation: (tx: any) => Promise
): Promise {
const claims = config.configurable?.userClaims as AgentUserClaims;

if (!claims || !claims.tenantId || !claims.userId) {
throw new Error('Unauthorized: Missing OIDC Claims in Agent Execution Context');
}

return await prisma.transaction(async (tx) => { await tx.executeRawSELECT set_config('app.current_tenant_id', ${claims.tenantId}, true);
await tx.$executeRawSELECT set_config('app.current_user_id', ${claims.userId}, true);

return await dbOperation(tx);

});
}

`

Es fundamental pasar true como último argumento de set_config para que las variables solo se apliquen al alcance de la transacción actual (SET LOCAL). Esta es una configuración clave para evitar que los permisos de un usuario anterior se filtren a la siguiente solicitud en un entorno con connection pooling de base de datos.

Ahora, crea una política en el archivo SQL que lea estas variables de sesión.

`sql
ALTER TABLE tenant_documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE tenant_documents FORCE ROW LEVEL SECURITY;

CREATE POLICY agent_tenant_isolation_policy ON tenant_documents
FOR ALL
TO authenticated_agent_role
USING (
tenant_id = current_setting('app.current_tenant_id', true)::uuid
)
WITH CHECK (
tenant_id = current_setting('app.current_tenant_id', true)::uuid
);

`

Con esta combinación, no importa qué consulta extraña genere el agente: no podrá ni siquiera consultar datos fuera del área de su propio tenant. Incluso si ocurre un incidente de filtración, el tiempo de recuperación de datos (MTTR) se puede reducir de varios días a solo unos minutos.

Pipeline de verificación CI/CD usando Promptfoo y PyRIT

No es viable probar manualmente cada vez que un desarrollador modifica el código de las herramientas del agente para comprobar si el aislamiento de permisos funciona correctamente. Integramos las herramientas de código abierto Promptfoo y PyRIT de Microsoft en GitHub Actions para realizar la verificación en cada PR.

`yaml
name: Agent Red Teaming Security Gate

on:
pull_request:
paths:
- 'src/agents/'
- 'src/tools/
'
- 'prompts/**'

jobs:
security-eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: 'npm'

  - name: Install Dependencies
    run: npm ci

  - name: Run Promptfoo Scan
    uses: promptfoo/promptfoo-action@v1
    with:
      config: 'promptfooconfig.yaml'
      openai-api-key: ${{ secrets.OPENAI_API_KEY }}
      github-token: ${{ secrets.GITHUB_TOKEN }}

  - name: Run PyRIT Multi-Turn Test
    env:
      AGENT_ENDPOINT: 'http://localhost:3000/api/agent'
    run: |
      python -m pip install pyrit
      python scripts/run_pyrit_eval.py --endpoint $AGENT_ENDPOINT --pass-threshold 0.98

`

En el entorno de ejecución del servidor, adjuntamos un middleware de circuit breaker que detiene la operación de inmediato si detecta patrones de amenaza.

`typescript
import { Request, Response, NextFunction } from 'express';

export class AgentCircuitBreaker {
private failureCount: number = 0;
private readonly threshold: number = 3;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';

private forbiddenSignatures: RegExp[] = [
/ignore\s+all\s+previous\s+instructions/i,
/system::override_privileges/i,
/grant\s+role\s+admin/i,
/concat\s*(\s*select/i
];

public middleware() {
return (req: Request, res: Response, next: NextFunction) => {
if (this.state === 'OPEN') {
return res.status(503).json({
error: 'CircuitBreaker:Open - Agent execution halted'
});
}

  const promptInput = JSON.stringify(req.body);
  const isPatternViolated = this.forbiddenSignatures.some(sig => sig.test(promptInput));

  if (isPatternViolated) {
    this.failureCount++;
    this.dispatchSecurityAlert(req.body);

    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
    }

    return res.status(403).json({
      error: 'Security Policy Violation: Malicious prompt pattern'
    });
  }

  next();
};

}

private dispatchSecurityAlert(payload: any): void {
const webhookUrl = process.env.SECURITY_WEBHOOK_URL;
if (!webhookUrl) return;

fetch(webhookUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    event: 'AGENT_PRIVILEGE_ESCALATION_DETECTED',
    timestamp: new Date().toISOString(),
    payload
  })
}).catch(() => {});

}
}

`

El orden para construir el pipeline de pruebas de seguridad es sencillo:

  1. Coloca promptfooconfig.yaml en la raíz del proyecto y define los elementos de inspección de jailbreak y excessive-agency.
  2. Vincula un flujo de trabajo de detección de PR en GitHub Actions para ejecutar los escenarios de Promptfoo y PyRIT.
  3. Coloca el middleware AgentCircuitBreaker al frente del endpoint de Express para detener la ejecución del agente al detectar patrones de inyección 3 veces consecutivas.

Al contar con esta configuración, puedes ahorrar más de 5 horas a la semana que antes dedicabas a la revisión manual de prompts.

Métrica de evaluación Método de verificación manual Adopción de automatización Zero-Trust
Tiempo de preparación de auditoría 5 a 8 horas por semana Menos de 1 hora por semana
MTTR en caso de filtración de permisos Varios días (investigación completa de registros/BD) Minutos (limitado al alcance de RLS)
Tasa de bloqueo de prompt injection Aprox. 23% 99.9%
Riesgo de facturación en caso de fuga de claves Facturación ilimitada en la nube Facturación bloqueada mediante TTL de 60s

La clave de la seguridad en agentes no radica en rezar para que el modelo se porte bien. Consiste en ponerle esposas a nivel de infraestructura para que, incluso si el modelo dice tonterías o cae en un ataque, no pueda causar daños al sistema.