AI Agent 项目在接入大模型、数据库、MCP 服务和第三方 API 后,配置管理会迅速成为安全短板。密钥硬编码、环境混用或日志泄露,都可能让一次普通提交演变为凭据暴露。要继续扩展业务能力,应先统一配置来源、环境边界、启动校验和泄露防护机制。
一个 AI Agent 项目通常会同时接入大模型、数据库、第三方工具和独立 MCP 服务。随着外部系统越来越多,API Key、数据库密码、JWT Secret 等敏感配置也会迅速分散到不同代码文件中。
在开发早期,把密钥直接写进代码看起来最省事,但这会带来几个长期问题:
因此,项目升级的第一个阶段不是增加更多业务功能,而是先建立敏感配置治理和运行安全基线。
本阶段的目标可以概括为:
真实秘密不进入代码,环境差异不依赖手动改源码,错误配置在启动阶段尽早失败,日志和提交链路提供最后一道泄露防线。
项目包含三个需要分别治理的运行单元:
flowchart TD
FRONTEND["Vue 前端"] -->|API 地址| BACKEND["Flask 后端"]
BACKEND -->|模型 API Key| LLM["大模型服务"]
BACKEND -->|账号和密码| MYSQL["MySQL"]
BACKEND -->|JWT Secret| JWT["身份凭证"]
BACKEND -->|MCP 调用| MCP["MCP 服务"]
MCP -->|天气 API Key| WEATHER["天气服务"]
如果这些配置直接散落在源代码中,会出现以下风险:
治理不能只做“把字符串挪到 .env”这一个动作,而要覆盖配置加载、启动校验、文件保护、日志、CI 和外部密钥轮换整个生命周期。
flowchart TD
OS["操作系统或部署平台环境变量"] --> LOAD["配置加载与优先级合并"]
SERVICE["服务目录 env 文件"] --> LOAD
ROOT["仓库根目录 env 文件"] --> LOAD
DEFAULT["非敏感默认值"] --> LOAD
LOAD --> ENV["根据 APP_ENV 选择配置"]
ENV -->|development| DEV["DevelopmentConfig"]
ENV -->|test| TEST["TestConfig"]
ENV -->|production| PROD["ProductionConfig"]
DEV --> VALIDATE["启动前配置校验"]
TEST --> VALIDATE
PROD --> VALIDATE
VALIDATE -->|通过| START["启动 Flask 或 MCP 服务"]
VALIDATE -->|缺失或不安全| FAIL["拒绝启动并报告变量名"]
配置优先级为:
操作系统 / 部署平台环境变量
> 服务目录 .env
> 仓库根目录 .env
> 代码中的非敏感默认值
真实密钥不提供代码默认值。代码默认值只用于端口、模型名称、日志级别等不敏感配置。
Flask 后端原本分散的配置被集中到 app/config.py,主要包括:
配置通过统一辅助方法读取:
def _env(name, default=""):
return os.getenv(name, default).strip()
对于端口、超时等整数配置,额外进行类型转换和错误提示:
def _env_int(name, default):
value = _env(name, str(default))
try:
return int(value)
except ValueError as exc:
raise RuntimeError(f"{name} must be an integer") from exc
这种集中式处理能够避免不同模块各自读取环境变量、默认值不一致和类型错误延迟到运行过程中才暴露。
项目使用 APP_ENV 选择运行环境:
APP_ENV=development
同时兼容常见缩写:
dev -> development
testing -> test
prod -> production
三套配置的职责如下:
| 环境 | 主要用途 | Debug | 凭据策略 |
|---|---|---|---|
| development | 本地开发 | 可开启 | 从本地 .env 读取真实开发凭据 |
| test | 自动化测试 | 关闭 | 允许明确标记为 test-only 的隔离值 |
| production | 正式部署 | 强制关闭 | 必须提供真实配置,拒绝示例占位值 |
APP_ENV 选择的是配置规则,而 .env 提供的是具体配置值。它们不是两套互相独立的配置来源,而是配合工作的:
.env 提供 MYSQL_HOST、JWT_SECRET 等值
│
▼
APP_ENV 决定使用 development、test 或 production 规则
│
▼
config.py 合并并校验后交给 Flask
项目不需要创建 development.env、production.env 才能完成环境切换。部署平台直接设置 APP_ENV 和对应环境变量即可。
配置错误越晚发现,排查成本越高。如果直到用户请求到来时才发现数据库密码或模型 Key 缺失,错误位置会远离真正原因。
因此,应用创建阶段会先执行配置校验:
selected_config.validate()
缺少必要配置时直接拒绝启动,并只输出缺失的变量名:
Missing required environment variables:
DEEPSEEK_API_KEY, MYSQL_PASSWORD, JWT_SECRET
错误信息不会输出变量实际值。
Production 还会拒绝明显的占位内容,例如:
change-me
replace-with
example
test-only
这可以防止把 .env.example 原样复制到生产环境后,服务仍然使用弱密码或示例密钥运行。
开发服务器原本容易出现两个问题:固定开启 Debug,以及默认所有网络接口。
本阶段调整为:
APP_HOST 和 APP_PORT 支持环境变量。127.0.0.1,避免本地服务无意暴露到局域网。示例:
APP_HOST=127.0.0.1
APP_PORT=8123
容器部署需要所有接口时,再显式配置:
APP_HOST=0.0.0.0
显式配置比默认暴露更容易理解,也更符合最小暴露原则。
需要注意,Flask 自带服务器仍然只适合开发。Production 后续应使用 Gunicorn 等 WSGI Server,并在反向代理后运行。
MCP 服务是独立运行进程,不能假设它一定和 Flask 使用同一个工作目录或启动方式,因此建立了独立配置模块。
主要配置包括:
APP_ENV=development
LOG_LEVEL=INFO
SENIVERSE_API_KEY=replace-with-your-weather-api-key
MCP_HOST=127.0.0.1
MCP_PORT=8000
MCP_PATH=/mcp
MCP 配置同样支持:
.env。.env。/ 开头。这样,天气 API Key 不再出现在 server.py 的工具实现中,工具代码只关心业务逻辑。
.env.example 如何既安全又可用真实 .env 不应该提交,但团队成员仍然需要知道项目依赖哪些配置。因此,每个运行单元都提供只包含变量名称和示例格式的模板:
zzx-ai-agent-backend/.env.example
zzx_mcp_server/.env.example
zzx-ai-agent-frontend/.env.example
zzx-ai-agent-frontend/.env.development.example
zzx-ai-agent-frontend/.env.production.example
模板中使用明确占位符:
DEEPSEEK_API_KEY=replace-with-your-model-api-key
MYSQL_PASSWORD=replace-with-your-database-password
JWT_SECRET=replace-with-a-long-random-jwt-secret
新环境的使用流程是:
复制 .env.example
│
▼
保存为 .env
│
▼
填写当前环境的真实配置
│
▼
设置 APP_ENV
│
▼
启动并通过配置校验
.env.example 是配置契约,不是可直接用于生产的配置文件。
根目录 .gitignore 需要同时满足两个目标:
当前规则为:
.env
.env.*
**/.env
**/.env.*
!.env.example
!**/.env.example
!**/.env.*.example
前面的规则屏蔽根目录及子目录中的真实 .env 和环境变体,后面的否定规则重新允许示例文件进入 Git。
因此:
.env -> 不提交
.env.production -> 不提交
service/.env -> 不提交
service/.env.local -> 不提交
.env.example -> 可以提交
.env.production.example -> 可以提交
service/.env.test.example -> 可以提交
运行日志也统一忽略:
*.log
logs/
需要强调:.gitignore 只能阻止尚未被 Git 跟踪的新文件。如果真实 .env 已经提交过,仅增加忽略规则并不会把它从历史中删除,更不会让其中的密钥自动失效。
Vite 通过 VITE_ 前缀向前端代码暴露环境变量:
VITE_API_BASE_URL=/api
这些值会在构建期间写入 JavaScript 产物,任何访问页面的人都可以查看。因此前端环境变量只能放置公开配置,例如:
不能放置:
Docker 构建通过参数接收公开 API 地址:
ARG VITE_API_BASE_URL=/api
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
RUN npm ci
RUN npm run build
依赖安装使用 npm ci,确保构建结果与 lock 文件保持一致,减少部署环境之间的依赖漂移。
敏感信息从代码中移除后,仍然可能通过日志泄露。例如:
Authorization: Bearer eyJ...
password=...
Cookie: refresh_token=...
用户问题:完整私人对话
工具输入:包含地址或账号
本阶段从两个层面处理日志。
最有效的日志脱敏是不要记录不必要的原始内容。
调整后的日志策略包括:
服务端增加统一 RedactingFilter,对常见敏感形式进行替换:
Bearer Token
Authorization
Cookie
API Key
JWT Secret
Password
Token 字段
过滤后的内容类似:
Authorization: [REDACTED]
Bearer [REDACTED]
password=[REDACTED]
flowchart TD
CODE["业务代码日志"] --> MIN["减少原始敏感内容"]
MIN --> FILTER["RedactingFilter"]
FILTER --> HANDLER["Console 或文件 Handler"]
HANDLER --> OUTPUT["脱敏后的日志"]
过滤器只是最后一道保护,不能替代合理的日志设计。对于用户对话、地址、账号等非固定格式隐私,仅依赖正则表达式很难完整识别,因此应优先避免记录原文。
本地 .gitignore 无法阻止所有误操作,例如开发者使用强制添加、把密钥粘贴到普通 Python 文件或提交到文档中。
因此,项目增加 Gitleaks 持续扫描:
flowchart TD
COMMIT["开发者提交代码"] --> PUSH["Push 或 Pull Request"]
PUSH --> CHECKOUT["检出完整 Git 历史"]
CHECKOUT --> SCAN["Gitleaks 扫描"]
SCAN -->|未发现秘密| PASS["CI 通过"]
SCAN -->|发现疑似秘密| BLOCK["CI 失败并阻止合并"]
扫描触发条件包括:
CI 使用完整历史检出:
with:
fetch-depth: 0
这不仅检查当前文件,还能发现历史提交中可能遗留的秘密。
如果一个 API Key 曾经进入代码或 Git 历史,应当按照“已经泄露”处理。
正确处置顺序是:
flowchart TD
FOUND["发现密钥进入代码或历史"] --> REVOKE["在外部平台立即吊销"]
REVOKE --> CREATE["创建权限最小化的新密钥"]
CREATE --> STORE["写入安全环境变量或 Secret Manager"]
STORE --> REMOVE["从当前代码和文档移除旧值"]
REMOVE --> SCAN["扫描仓库与历史"]
SCAN --> HISTORY["判断合规是否要求清理历史"]
HISTORY -->|是| REWRITE["规划 Git 历史重写"]
HISTORY -->|否| DONE["保留已失效记录并持续扫描"]
必须在对应平台完成的动作包括:
Git 历史重写会影响所有协作者和现有分支,不应在没有协调的情况下直接执行。大多数情况下应先完成密钥轮换,让旧记录失去实际价值,再根据合规要求决定是否重写历史。
以全新开发环境为例:
复制配置模板:
cp zzx-ai-agent-backend/.env.example zzx-ai-agent-backend/.env
填写当前开发环境的模型、MySQL 和 JWT 等配置。
cp zzx_mcp_server/.env.example zzx_mcp_server/.env
填写天气服务 Key 和 MCP 配置。
cp zzx-ai-agent-frontend/.env.example zzx-ai-agent-frontend/.env
前端仅配置公开的 API 地址,不写入任何秘密。
APP_ENV=development
正式部署时由部署平台设置:
APP_ENV=production
不需要通过修改 config.py 完成切换。
本阶段完成后,对以下内容进行了验证:
阶段 0.1 建立的是项目安全基线,不是完整的企业级 Secret Management 平台。
当前方案解决了:
.env 误提交风险。后续还可以继续完善:
敏感配置治理不是把 Key 从 Python 文件移动到 .env 就结束了,而是一套贯穿开发、测试、部署、日志、Git 和凭据生命周期的工程实践。
本阶段通过环境变量、分环境配置、启动校验、示例模板、Git 忽略、日志最小化和 Gitleaks 扫描,建立了项目后续演进所需的第一层安全边界。
只有先确保秘密不会随着代码和日志到处扩散,后续的 JWT 认证、Redis 会话、RAG 管理和多 Agent 协作才有可靠的运行基础。