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

为什么不能给 Node.js Agent 主密钥,以及 60 秒临时 Token 的实现方法

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

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

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

관련 영상

Ship 26 NYC - 不要把主密钥交给你的智能体17:22

Ship 26 NYC - 不要把主密钥交给你的智能体

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
구독 채널
비디오
커뮤니티
로그인

为什么不能给 Node.js Agent 主密钥,以及 60 秒临时 Token 的实现方法

在使用 LangChain 或 LlamaIndex 构建自主型 Agent(智能体)时,终究会迎来连接数据库和外部 API 的那一刻。而事故通常就在此时发生。一次 Prompt Injection(提示词注入)攻击,就可能让写在 .env 里的 OpenAI 主密钥或数据库管理员密码瞬间泄露。无论把 Prompt Guardrails(提示词 Guardrails)设计得多么严密也都无济于事。只要 LLM 的推理层与执行层绑在一起,仅凭一句话,安全性就会脆弱地被击穿。

根据谷歌 2025 年 SAIF(Secure AI Framework)报告显示,引入 Agent 的企业中有 88% 遭遇过提示词注入尝试。而传统的文本模式检测技术拦截率仅为 23%。与其抱有侥幸心理,倒不如干脆不信任 Agent 进程本身。应该完全不向 Agent 授予权限,而是改为在中间件层注入仅存活 60 秒的临时 Token 的架构。

在 Express 中实现 60 秒 Dynamic Vault Token 注入

使用 HashiCorp Vault 的 AppRole 认证,即可实现在调用工具的极短暂瞬间颁发一个有效期仅 60 秒的 Token。当 Agent 请求外部 API 时,拦截器在中间介入,并将 Short-lived Token 塞入 Header 中。

`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;
  }
};

}
}

`

光写好代码还不够。由于 Node.js V8 引擎的堆内存(Heap Memory)是由垃圾回收器(GC)按自己的机制运行的,因此即便清空了变量,Token 字符串仍会在堆中残留一段时间。如果遭遇 Heap Dump 攻击,就会有泄露风险。

  1. 收到 API 响应后,立即将敏感值存入 Buffer 对象。
  2. 执行完毕后,运行 Buffer.fill(0) 强制将字节清理为 0。
  3. 将引用变量赋值为 null,交给 GC 处理。

只要做好这三个步骤,通过内存剖析(Memory Profiling)导致 Token 泄露的概率就会大幅降低。

凭据管理方式 平均生存时间 (TTL) 被窃取时的受害范围 审计追踪
硬编码 Master API Key 无限制 掌控全部基础设施权限 共享单一 Key,无法识别主体
环境变量 (.env) Static Secret 进程生命周期 滥用该 Node 进程的所有权限 无法追踪具体用户的工作流
Vault AppRole Ephemeral Token 60秒 仅限该 Tool 执行 1 次 通过 Vault Audit Log 进行秒级追踪

将 Auth0 OIDC Claim 传递至 PostgreSQL RLS 策略

即使 Agent 因提示词越狱而执行“查询所有会员信息”的指令,只要在数据库引擎层面予以拒绝即可。如果是 Multi-tenant(多租户)SaaS 环境,则需要设置 PostgreSQL 的 Row Level Security (RLS)。

获取 Auth0 签发的 JWT 中的 userId 和 tenantId,并将其传递给 LangChain 的 RunnableConfig 会话上下文。在 Prisma 事务中,将该上下文作为 Session 变量注入。

`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);

});
}

`

set_config 的最后一个参数必须传入 true,变量才会仅应用于当前事务范围(SET LOCAL)。这是在数据库连接池(Connection Pooling)环境中,防止上一用户的权限泄漏到下一请求的关键配置。

现在,在 SQL 文件中创建读取该 Session 变量的策略。

`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
);

`

配置好这套组合拳后,无论 Agent 生成多么离谱的查询,都无法查到自己租户范围之外的数据。即便发生泄露事故,也能将数据恢复时间(MTTR)从几天缩短至短短几分钟。

使用 Promptfoo 和 PyRIT 构建 CI/CD 验证流水线

开发者每次修改 Agent 工具代码时,不可能每次都手动测试权限隔离是否正常运行。我们可以将开源工具 Promptfoo 和微软的 PyRIT 整合到 GitHub Actions 中,以 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

`

在服务器运行时(Server Runtime)中,挂载一个熔断器(Circuit Breaker)中间件,以便在检测到威胁模式时立即停止运行。

`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(() => {});

}
}

`

构建安全测试流水线的步骤非常简单:

  1. 在项目根目录下放置 promptfooconfig.yaml,并定义 jailbreak 和 excessive-agency 检查项。
  2. 在 GitHub Actions 中设置 PR 监听工作流,运行 Promptfoo 和 PyRIT 测试场景。
  3. 在 Express 端点前部署 AgentCircuitBreaker 中间件,当连续 3 次检测到注入模式时,暂停 Agent 的执行。

建立这套架构后,每周可以节省下原本花在手动审查提示词上的 5 小时以上的时间。

评估指标 手动验证方式 引入 Zero-Trust 自动化
审计准备时间 每周 5~8 小时 每周不足 1 小时
权限泄露时的 MTTR 数天(全量日志/数据库全面排查) 数分钟(限制在 RLS 范围内)
提示词注入拦截率 约 23% 99.9%
Key 泄露时的扣费风险 无限制的云服务扣费 60 秒 TTL 阻断扣费

Agent 安全的核心不在于祈祷模型听话,而是在基础设施层面戴上“手铐”,即使模型胡言乱语或被攻击控制,也无法对系统造成实质伤害。