企业级项目 env 配置安全:从 .env 到 AI 项目密钥治理
1. 引言:为什么企业级项目不能随意管理 .env
在个人项目或课程 Demo 中,很多人会把配置写进 .env,再通过 dotenv、框架配置系统或运行环境读取。这样做在本地开发阶段很方便:变量集中、修改简单、不会把配置硬编码进业务代码。
但在真正的企业级项目中,.env 绝不能只被理解成“放配置的文件”。企业关注的不是“程序能不能读到变量”,而是以下问题:
- 谁能看到这些配置?
- 哪些配置是普通配置,哪些是敏感密钥?
- 开发、测试、预发、生产是否严格隔离?
- 密钥泄露后能否快速吊销和轮换?
- 谁在什么时候修改过配置,是否可审计?
- CI/CD、日志、Docker 镜像、构建产物中是否会意外泄露密钥?
- AI 项目中 prompt、completion、embedding、RAG 数据、工具调用日志是否也会携带敏感信息?
所以,企业级 .env 配置安全的核心不是“把变量放在哪里”,而是建立一套围绕配置和密钥的治理体系:分类、隔离、注入、校验、审计、轮换、应急响应。
配置泄露的影响也往往不是小问题。数据库连接串泄露可能导致拖库,云厂商 AK/SK 泄露可能导致资源被盗刷,第三方 API Key 泄露可能导致接口被滥用,AI 项目的模型 Key 泄露还可能造成高额 token 费用、客户数据外泄和合规风险。
2. 基础概念:配置、环境变量、密钥不是一回事
很多初学者会把“配置”“环境变量”“.env”“Secret”混为一谈,但企业开发中必须区分它们。
2.1 配置 Configuration
配置是影响程序行为的参数,例如:
- 服务端口:PORT=3000
- 日志级别:LOG_LEVEL=info
- 当前运行环境:APP_ENV=production
- 请求超时时间:REQUEST_TIMEOUT_MS=10000
- 是否开启某个功能:FEATURE_X_ENABLED=false
配置不一定敏感。比如端口号通常不是密钥,但错误配置仍可能导致线上事故。
2.2 环境变量 Environment Variable
环境变量是向程序注入配置的一种方式。程序启动时可以从操作系统进程环境中读取变量,例如 Node.js 中的 process.env.PORT。
环境变量本身不是安全机制。它只是传递配置的通道。变量是否安全,取决于:
- 值是否敏感。
- 谁能读取运行环境。
- 是否被写入日志、构建产物或镜像层。
- 是否有访问控制、审计和轮换机制。
2.3 Secret / 密钥
Secret 是具有访问能力的敏感值,例如:
- 数据库密码:DB_PASSWORD
- 数据库连接串:DATABASE_URL
- JWT 签名密钥:JWT_SECRET
- 第三方平台 API Key:PAYMENT_API_KEY
- 云厂商 AK/SK:CLOUD_ACCESS_KEY_ID、CLOUD_SECRET_ACCESS_KEY
- LLM Provider API Key:LLM_API_KEY
- Vector DB Token:VECTOR_DB_API_KEY
Secret 一旦泄露,攻击者可能直接访问数据、调用接口、伪造身份或消耗资源。
2.4 .env
.env 是一种常见的本地开发配置文件格式。它适合本地快速启动项目,但不等于企业级 Secret 管理系统。
企业中可以使用 .env.example 帮助开发者了解变量清单,但真实值通常应由 Secret Manager、Vault、KMS、CI Secrets、Kubernetes Secret 或配置中心在运行时注入。
| 普通配置 | APP_ENV、PORT、LOG_LEVEL | 通常不敏感 | 配置文件、环境变量、配置中心 |
| 半敏感配置 | INTERNAL_API_BASE_URL | 视情况而定 | 环境隔离、访问控制 |
| Secret | DB_PASSWORD、JWT_SECRET、API_KEY | 敏感 | Secret Manager / Vault / CI Secret |
3. 企业级 .env / Secret 管理核心原则
3.1 永远不要提交真实 .env
真实 .env 文件通常包含数据库密码、Token、私钥、内部服务地址等内容。一旦提交到 Git,即使后来删除,历史记录中仍可能保留泄露副本。
正确做法是:
- 仓库提交 .env.example 或 .env.template。
- 本地开发者自行创建 .env.local。
- 生产、预发、测试环境的真实密钥由平台注入。
- 使用 Git secret scanning、pre-commit hook 或 CI 扫描阻止密钥进入仓库。
3.2 不同环境必须隔离
企业项目通常至少区分以下环境:
| local | 开发者本机 | 可以使用本地 .env.local,不能复用生产密钥 |
| development | 联调环境 | 使用开发环境数据库和低权限密钥 |
| test | 自动化测试 | 使用测试专用资源,数据可重置 |
| staging | 预发环境 | 尽量接近生产,但密钥仍应独立 |
| production | 生产环境 | 最高安全要求,必须审计、轮换、最小权限 |
低环境泄露不应该影响生产。因此,不要让 local、dev、test、staging、production 共用同一组数据库密码、JWT 密钥或第三方 API Key。
3.3 最小权限
最小权限原则要求:一个服务只拿自己真正需要的密钥。
例如,一个订单服务可能需要支付接口 Key,但不应该拥有用户中心数据库的管理员密码。一个只负责向量检索的服务需要读取向量库,但不应该拥有删除整个索引的管理员权限。
企业中常见拆分方式包括:
- 按服务拆分密钥。
- 按环境拆分密钥。
- 按用途拆分密钥。
- 按租户拆分密钥。
- 使用服务账号而不是个人账号。
3.4 密钥必须可轮换、可吊销、可审计
如果一个密钥泄露后无法快速吊销,或者不知道哪些服务使用它,这就是治理失败。
企业级 Secret 管理至少应该回答:
- 谁创建了这个密钥?
- 哪些服务能读取它?
- 它什么时候被读取过?
- 它多久轮换一次?
- 泄露后如何吊销?
- 新旧密钥如何平滑切换?
3.5 密钥值不进入 Git、日志、镜像层和制品
密钥不应该出现在:
- Git 仓库和 Git 历史。
- CI/CD 日志。
- Dockerfile。
- Docker 镜像层。
- 前端构建产物。
- 错误堆栈和日志平台。
- 工单、聊天记录、截图、文档。
- AI prompt、completion 和调试追踪中。
4. 推荐仓库结构与 .env.example 示例
企业项目中,仓库里应该提交“变量清单”和“安全占位符”,而不是提交真实值。
推荐结构如下:
project-root/
├─ .env.example # 可提交,只放变量名和安全占位符
├─ .env.local # 本地个人配置,不提交
├─ .gitignore
├─ src/
└─ README.md
推荐 .gitignore:
.env
.env.*
!.env.example
!.env.test.example
这段规则的含义是:
- 忽略 .env。
- 忽略 .env.local、.env.production、.env.staging 等真实配置文件。
- 允许提交 .env.example 和 .env.test.example 这类安全模板。
示例 .env.example:
APP_ENV=local
PORT=3000
LOG_LEVEL=info
DATABASE_URL=__SET_IN_SECRET_MANAGER__
REDIS_URL=__SET_IN_SECRET_MANAGER__
JWT_SECRET=__SET_IN_SECRET_MANAGER__
THIRD_PARTY_API_BASE_URL=https://api.example.com
THIRD_PARTY_API_KEY=__SET_IN_SECRET_MANAGER__
.env.example 的价值是让新人知道项目启动需要哪些变量,而不是告诉他真实密钥是什么。真实值应通过本地安全渠道、企业 Secret Manager、CI Secret 或运行平台注入。
需要注意:前端项目中以 PUBLIC_、NEXT_PUBLIC_、VITE_ 等前缀暴露的变量通常会进入浏览器构建产物,因此不能放任何密钥。
错误示例:
VITE_PAYMENT_SECRET_KEY=__DO_NOT_PUT_SECRET_IN_FRONTEND__
正确思路是:前端只持有公开配置,真正的密钥由后端服务保存和调用。
5. 应用启动时的配置校验
企业项目不能等线上流量进来后才发现配置缺失。配置错误应该在应用启动阶段就失败,也就是 fail fast。
以 TypeScript + Zod 为例,可以把环境变量集中校验:
import { z } from "zod"
const envSchema = z.object({
APP_ENV: z.enum(["local", "development", "test", "staging", "production"]),
PORT: z.coerce.number().int().positive().default(3000),
DATABASE_URL: z.string().min(1),
JWT_SECRET: z.string().min(32),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info")
})
export const env = envSchema.parse(process.env)
然后业务代码统一从 env 对象读取配置,而不是到处直接访问 process.env:
import { env } from "./env"
app.listen(env.PORT)
这样做有几个好处:
- 必填变量缺失时,服务启动立即失败。
- 变量类型集中转换,例如把字符串端口转换成数字。
- 生产环境不会意外使用弱默认值。
- 业务代码不需要反复处理 undefined。
- 变量清单可以和 .env.example 对齐。
更严格的企业项目还会区分不同环境的校验规则。例如本地可以允许某些 Mock 配置,生产必须要求真实 Secret:
const parsedEnv = envSchema.parse(process.env)
if (parsedEnv.APP_ENV === "production" && parsedEnv.JWT_SECRET.length < 64) {
throw new Error("JWT_SECRET is too weak for production")
}
export const env = parsedEnv
这里要注意:启动错误可以提示“哪个变量缺失或不合法”,但不要把变量真实值打印出来。
6. CI/CD 中如何安全注入密钥
企业流水线中,密钥不应该写进 YAML 文件本身,而应该保存在 CI/CD 平台的 Secrets 配置区。Pipeline 运行时再临时注入为环境变量。
通用 CI/CD 示例:
name: deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu–latest
env:
APP_ENV: production
DATABASE_URL: ${{ secrets.DATABASE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
steps:
– uses: actions/checkout@v4
– run: npm ci
– run: npm test
– run: npm run build
不同平台的语法会有差异,例如 GitHub Actions、Gitee Actions、GitLab CI、Jenkins、云厂商流水线都有自己的写法。但核心思想一致:
- Secret 存在平台密钥区。
- 运行时注入。
- 不提交到 Git。
- 不输出到日志。
- 不写入构建产物。
CI/CD 中常见危险写法包括:
steps:
– run: echo "DATABASE_URL=$DATABASE_URL"
– run: docker build ––build–arg JWT_SECRET=$JWT_SECRET .
第一行会把密钥打印进日志。第二行可能让密钥进入构建历史或镜像层。
更安全的思路是:镜像构建阶段只构建代码和依赖,运行阶段再由部署平台注入密钥。
steps:
– run: docker build –t your–service:${{ github.sha }} .
– run: npm run deploy
如果确实需要在部署命令中使用密钥,也要确认平台日志会自动脱敏,并避免使用 set -x、printenv、env 等会打印全部变量的命令。
7. Docker / Docker Compose / Kubernetes 中的配置与密钥
7.1 Docker 本地运行
本地学习或开发时,可以使用 –env-file:
docker run –env-file .env.local your-service:local
但生产环境不应依赖开发者手动维护的 .env.production 文件,而应由部署平台、Secret Manager 或编排系统注入。
7.2 Docker Compose
本地开发可以使用 Compose 的 env_file:
services:
api:
image: your–service:local
env_file:
– .env.local
ports:
– "3000:3000"
这适合本地开发,但不代表生产密钥已经被安全治理。生产环境仍应考虑访问控制、审计、轮换和平台级注入。
7.3 Kubernetes
Kubernetes 通常用 ConfigMap 管普通配置,用 Secret 管敏感配置。
apiVersion: v1
kind: Secret
metadata:
name: app–secrets
type: Opaque
stringData:
DATABASE_URL: "__SET_IN_SECRET_MANAGER_OR_EXTERNAL_SECRET__"
JWT_SECRET: "__SET_IN_SECRET_MANAGER_OR_EXTERNAL_SECRET__"
—
apiVersion: v1
kind: ConfigMap
metadata:
name: app–config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
Pod 中引用示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: your–service
spec:
replicas: 2
selector:
matchLabels:
app: your–service
template:
metadata:
labels:
app: your–service
spec:
containers:
– name: api
image: your–service:__IMAGE_TAG__
envFrom:
– configMapRef:
name: app–config
– secretRef:
name: app–secrets
需要明确:Kubernetes Secret 本身不是企业密钥治理的终点。成熟企业还会关注:
- etcd 加密。
- RBAC 最小权限。
- 谁能读取 Secret。
- Secret 访问审计。
- 密钥轮换自动化。
- External Secrets、Vault 或云厂商 Secret Manager 集成。
8. 密钥生命周期:创建、分发、使用、轮换、吊销、审计
企业级密钥管理强调生命周期,而不是只强调保存位置。
8.1 创建
生产密钥不应该由开发者随手在本地生成后复制到各处。更推荐通过以下方式创建:
- 云控制台或 Secret Manager。
- Vault / KMS。
- 安全团队批准的服务账号。
- 自动化基础设施脚本。
密钥命名也应规范,例如:
your-service/production/database-url
your-service/production/jwt-secret
your-service/staging/llm-api-key
8.2 分发
密钥分发应该通过受控系统完成,而不是通过聊天软件、截图、邮件或文档。
常见方式包括:
- CI/CD Secret 注入。
- Secret Manager 按服务账号授权读取。
- Kubernetes External Secrets 同步。
- Vault Agent 注入。
- 平台运行时环境变量注入。
8.3 使用
应用程序通常只需要在运行时读取密钥,并在内存中使用。不要把密钥写入临时文件、日志、缓存、前端页面或错误响应。
8.4 轮换
密钥轮换包括定期轮换和事件触发轮换。
常见触发条件:
- 人员离职或权限调整。
- 供应商安全事件。
- 日志误打印。
- Git 提交泄露。
- CI/CD 日志泄露。
- 生产访问异常。
对于数据库密码、JWT 密钥等核心 Secret,需要设计平滑轮换策略。例如 JWT 可以支持一段时间内同时验证旧密钥和新密钥,但只用新密钥签发新 token。
8.5 吊销
发现泄露后,第一优先级不是“删除泄露文件”,而是让旧密钥立即失效。因为泄露内容可能已经被复制、缓存或同步到其他地方。
8.6 审计
审计关注的是:
- 谁创建了密钥。
- 谁修改了密钥。
- 哪个服务读取了密钥。
- 什么时候读取。
- 是否出现异常读取频率。
- 是否存在越权访问。
泄露应急流程可以这样设计:
发现疑似泄露
→ 立即吊销或禁用旧密钥
→ 生成新密钥并更新 Secret Manager
→ 重新部署服务
→ 检查访问日志和异常账单
→ 清理 Git 历史 / CI 日志 / 镜像制品中的泄露副本
→ 复盘并补充扫描规则
9. 日志与错误处理中的 Secret 泄露风险
很多密钥泄露不是因为开发者主动提交 .env,而是因为日志、报错、调试工具或监控系统记录了敏感值。
错误示例:
logger.error("failed to connect database", {
databaseUrl: process.env.DATABASE_URL,
error
})
这会把完整数据库连接串写入日志。连接串里可能包含用户名、密码、主机、库名等信息。
更安全的做法是只记录排障所需的非敏感字段:
logger.error("failed to connect database", {
host: new URL(process.env.DATABASE_URL!).hostname,
errorName: error instanceof Error ? error.name : "UnknownError"
})
企业项目中建议使用 allowlist 思路:只允许明确安全的字段进入日志,而不是把整个对象展开打印。
危险示例:
logger.info({ env: process.env })
logger.error({ requestHeaders: req.headers })
logger.debug({ config })
更安全的脱敏示例:
const SECRET_PATTERNS = [
// 规则1:匹配 Bearer Token,例如 Authorization: Bearer sk-xxxxxx
/Bearer\\s+[A–Za–z0–9._–]+/g,
/(api[_-]?key|token|secret|password)=([^&\\s]+)/gi,
/(DATABASE_URL|REDIS_URL|JWT_SECRET)=([^\\s]+)/g
]
function redactText(input: string): string {
return SECRET_PATTERNS.reduce(
(text, pattern) => text.replace(pattern, "$1=[REDACTED]"),
input
)
}
logger.info({
requestId,
message: redactText(message)
})
上面的示例展示的是思路:生产项目通常还应结合日志框架、网关、APM、错误追踪平台提供的脱敏能力,并通过测试确保敏感字段不会进入日志。
10. AI 项目的额外特殊规范
AI 项目不是“普通项目多一个 API Key”这么简单。它的特殊性在于:模型调用链路里会经过 prompt、completion、embedding、RAG 文档、向量数据库、工具调用、Agent 执行日志等数据,而这些内容都可能包含敏感信息。
AI 项目还常常按 token、模型等级、上下文长度、并发量计费。因此它不仅有传统安全风险,还有费用失控和数据合规风险。
10.1 LLM Provider API Key 管理
AI 项目的模型 API Key 应该和普通第三方 API Key 一样进入 Secret Manager、Vault、CI Secret 或运行平台密钥区。
推荐原则:
- local、dev、staging、production 使用不同 Key。
- Chat、Embedding、Eval、Batch Job 使用不同 Key。
- 生产禁止使用开发者个人 Key。
- Key 绑定服务账号或组织级账号。
- Key 权限尽量最小化。
- 设置调用额度、速率限制和告警。
Provider-neutral 的 .env.example 可以这样写:
LLM_PROVIDER=example-provider
LLM_MODEL=example-chat-model
LLM_API_KEY=__SET_IN_SECRET_MANAGER__
EMBEDDING_PROVIDER=example-provider
EMBEDDING_MODEL=example-embedding-model
EMBEDDING_API_KEY=__SET_IN_SECRET_MANAGER__
VECTOR_DB_URL=__SET_IN_SECRET_MANAGER__
VECTOR_DB_API_KEY=__SET_IN_SECRET_MANAGER__
LLM_MAX_OUTPUT_TOKENS=4096
LLM_REQUEST_TIMEOUT_MS=60000
LLM_MONTHLY_BUDGET_USD=500
LLM_PROMPT_LOGGING_MODE=metadata_only
MODEL_DATA_RETENTION_POLICY=zero_retention_required
这里的重点不是某个具体模型供应商,而是配置设计:模型供应商、模型名、API Key、向量库凭证、超时、token 上限、预算、日志模式和数据保留策略都应该显式配置并受控。
10.2 Chat Key、Embedding Key、Eval Key 分离
很多 AI 项目会同时做:
- 在线对话。
- 文档 embedding。
- 离线评测。
- 批量总结。
- Agent 工具调用。
如果所有任务共用一个高权限 Key,一旦泄露就会影响所有能力。更好的做法是按用途拆分:
| 在线对话 | LLM_API_KEY | 限制模型、限额、限流 |
| Embedding | EMBEDDING_API_KEY | 只允许 embedding 相关能力 |
| 离线评测 | EVAL_LLM_API_KEY | 可低优先级、独立预算 |
| 后台批处理 | BATCH_LLM_API_KEY | 独立队列和预算 |
10.3 模型白名单与费用控制
AI 项目中,不能让用户从前端任意传入模型名、max tokens 或 temperature 等关键参数。
错误示例:
const result = await llm.chat({
model: req.body.model,
maxTokens: req.body.maxTokens,
messages: req.body.messages
})
更安全的做法是在服务端维护模型白名单和参数上限:
const MODEL_ALLOWLIST = new Set(["example-chat-model", "example-fast-model"])
const requestedModel = String(req.body.model ?? "example-chat-model")
const model = MODEL_ALLOWLIST.has(requestedModel) ? requestedModel : "example-chat-model"
const maxTokens = Math.min(Number(req.body.maxTokens ?? 1024), env.LLM_MAX_OUTPUT_TOKENS)
const result = await llm.chat({
model,
maxTokens,
messages: req.body.messages
})
企业项目还应做:
- 用户级限流。
- 租户级限流。
- 月度预算。
- 单次请求 token 上限。
- 高价模型审批。
- 异常调用告警。
10.4 Prompt / Completion 也是敏感数据
AI 项目的 prompt 可能包含:
- 用户个人信息。
- 客户合同。
- 源码片段。
- 内部文档。
- 数据库查询结果。
- API Key 或 Token。
- 工具调用参数。
因此,生产环境不应该默认记录完整 prompt 和 completion。更推荐默认记录 metadata:
- requestId
- tenantId
- userId
- model
- token usage
- latency
- status
- error type
如确实需要采样保存 prompt 用于调试或评测,也应满足:
- 明确用户授权或企业合规依据。
- 脱敏后保存。
- 采样比例受控。
- 保留时间短。
- 可查询、可删除、可审计。
AI 请求日志脱敏示例:
const SECRET_PATTERNS = [
/sk-[A-Za-z0-9_-]{20,}/g,
/Bearer\\s+[A-Za-z0-9._-]+/g,
/(api[_-]?key|token|secret|password)=([^&\\s]+)/gi
]
function redactText(input: string): string {
return SECRET_PATTERNS.reduce(
(text, pattern) => text.replace(pattern, "[REDACTED]"),
input
)
}
function safeLogLLMRequest(req: {
requestId: string
tenantId: string
model: string
prompt?: string
}) {
logger.info({
requestId: req.requestId,
tenantId: req.tenantId,
model: req.model,
promptPreview: req.prompt ? redactText(req.prompt).slice(0, 200) : undefined
})
}
10.5 RAG / 向量数据库凭证安全
RAG 项目通常会引入文档解析、embedding、向量库写入、向量检索、答案生成等链路。不同链路需要不同权限。
推荐拆分:
| ingestion worker | 写入文档、写入向量 | 删除全部租户数据 |
| retrieval service | 读取当前租户向量 | 读取其他租户数据 |
| admin job | 索引维护 | 在线回答用户问题 |
| eval job | 读取评测集 | 访问生产客户原始数据 |
RAG 多租户项目中,tenantId 必须来自服务端认证上下文,而不是来自用户请求体。
const docs = await vectorDb.search({
query,
filter: { tenantId: req.body.tenantId }
})
上面这种写法是危险的,因为用户可以伪造 tenantId 越权检索。
更安全的写法:
const tenantId = req.auth.tenantId
const docs = await vectorDb.search({
query,
filter: {
tenantId,
visibility: "active"
}
})
10.6 模型数据保留与合规
企业 AI 项目需要确认模型供应商或运行平台的数据策略:
- prompt 和 completion 是否会被保留?
- 是否会用于训练?
- 数据保留多久?
- 是否支持 zero retention 或企业数据协议?
- 是否涉及跨境传输?
- 是否满足行业合规要求?
- 删除请求是否可执行、可审计?
这些内容不应该由开发者凭感觉决定,而应由技术、法务、安全、合规和业务共同确认。
10.7 BYOK 场景
BYOK 指 Bring Your Own Key,客户自带模型 Key 或加密 Key。它常见于 ToB SaaS、私有化部署和多租户 AI 平台。
BYOK 的安全要求包括:
- 每个租户 Key 独立保存。
- Key 加密存储。
- 运行时按租户动态读取。
- 租户可自行轮换和吊销。
- 服务端不能把 A 租户 Key 用到 B 租户请求中。
- 日志中不能出现客户 Key。
10.8 Agent / MCP / 工具调用的密钥边界
AI Agent 项目往往会连接 Git、数据库、工单系统、聊天工具、浏览器、云资源等外部工具。此时风险不只来自模型 API Key,还来自工具凭证。
关键原则:
- 工具凭证不能写进 prompt。
- 模型不应该直接看到原始 Secret。
- 工具调用应由服务端按权限执行。
- 高风险操作需要审批或策略限制。
- 不同工具使用不同服务账号。
- 工具执行日志必须脱敏。
例如,Agent 可以请求“查询某个工单”,但真正的工单系统 Token 应由后端工具层保存和使用,而不是作为文本塞进模型上下文。
11. 常见反模式与正确做法
| 把 .env 提交到 Git | Git 历史长期保留,泄露后难清理 | 提交 .env.example,真实值进 Secret Manager |
| 生产使用开发者个人 Key | 人员离职或权限变化会影响生产 | 使用服务账号或组织级凭证 |
| 所有环境共用一个数据库密码 | 低环境泄露可影响生产 | 按环境隔离密钥和权限 |
| 在日志中打印 process.env | 密钥进入日志平台 | 结构化日志 allowlist + 脱敏 |
| Dockerfile 写入 ENV SECRET=xxx | 密钥进入镜像层 | 运行时注入 Secret |
| 前端环境变量保存 Secret | 浏览器产物可被用户看到 | Secret 只放后端或平台密钥区 |
| CI 中 echo $TOKEN | Token 进入流水线日志 | 避免打印,依赖平台脱敏能力 |
| .env.example 写真实示例密码 | 新人可能照抄,甚至误连共享资源 | 只写安全占位符 |
| AI 项目记录完整 prompt | prompt 可能包含 PII、合同、源码、密钥 | 默认 metadata_only,必要时脱敏采样 |
| RAG tenantId 来自前端 | 用户可伪造 tenantId 越权检索 | tenantId 来自认证上下文 |
| 用户可传任意 model id | 成本和数据策略失控 | 服务端模型白名单 |
| Agent 工具 Token 进入 prompt | 模型上下文和日志可能泄露凭证 | Token 留在服务端工具层 |
12. 企业落地检查清单
- 是否只提交 .env.example,不提交真实 .env?
- .gitignore 是否覆盖 .env、.env.* 并保留示例文件?
- local、development、test、staging、production 是否使用不同密钥?
- 生产密钥是否由 Secret Manager / Vault / CI Secret 管理?
- 应用启动时是否校验必要配置?
- 生产环境是否禁止弱默认密钥?
- 日志是否避免输出环境变量、连接串、Token 和请求头?
- 日志平台、错误追踪平台和 APM 是否配置脱敏规则?
- 密钥是否有轮换、吊销、审计流程?
- CI/CD 是否避免在日志和制品中泄露密钥?
- Docker 镜像是否避免把密钥写入构建层?
- Kubernetes Secret 是否配合 RBAC、审计、加密和外部密钥系统?
- AI Key 是否按环境、用途、租户隔离?
- Prompt / Completion 是否默认不记录全文?
- RAG / Vector DB 是否做租户隔离和最小权限?
- 是否设置 token、费用、并发和模型白名单?
- Agent / 工具调用凭证是否留在服务端工具层,而不是进入模型上下文?
- 密钥泄露时是否有明确应急流程?
13. 总结
企业级 .env 配置安全的重点,不是简单地把变量放进某个文件,而是建立完整的配置和密钥治理体系:配置分类、环境隔离、最小权限、运行时注入、启动校验、日志脱敏、轮换吊销、审计追踪和泄露应急。
.env 更适合作为本地开发便利工具;.env.example 适合作为安全变量模板;生产环境的真实 Secret 应进入 Secret Manager、Vault、KMS、CI Secret、Kubernetes Secret 或企业配置平台,并通过权限和审计进行治理。
AI 项目还需要额外关注 prompt、completion、embedding、RAG、向量数据库、模型调用、Agent 工具凭证、数据保留和 token 费用控制。真正成熟的 AI 工程实践,不只是能调用模型,而是能在安全、合规、成本和可维护性之间建立稳定边界。
网硕互联帮助中心




评论前必须登录!
注册