把 MCP 从本地搬到服务器后,我才发现:真正危险的不是端口暴露,而是这 3 个权限漏洞
把 MCP 从本地搬到服务器后,我才发现:真正危险的不是端口暴露,而是这 3 个权限漏洞
我把原本只在本机运行的 MCP 工具搬到服务器后,AI 客户端第一次连接就成功了。
查询数据正常,读取文件正常,删除临时文件也正常。直到我换了一台电脑,用同一个 Token 再次调用删除工具,才意识到一个比“端口有没有暴露”更严重的问题:
服务虽然上线了,但权限系统实际上根本不存在。一个共享 Token 可以调用全部工具;普通查询和删除文件没有权限差别;日志里所有请求都来自同一个身份。谁做了什么、为什么被允许、密钥泄漏后如何止损,系统全都回答不了。
这也是很多本地 MCP 远程化改造最容易踩的坑:大家把注意力放在 HTTPS、反向代理和接口连通性上,却忽略了一个根本变化——
MCP 从本机搬到服务器,改变的不只是访问地址,而是整个系统的信任边界。
本文不再重复 MCP 的基础概念和远程部署步骤,而是专门复盘部署完成后,最容易漏掉的三个安全闭环:权限、密钥和审计日志。
---
服务跑通了,但原来的安全模型已经失效
本地 MCP 看起来没有复杂的认证系统,却不一定等于完全没有安全保护。
它依赖的是一组“隐形门禁”:
- 只有登录本机的用户能启动客户端;
- MCP Server 通常只监听本地接口;
- 文件权限由操作系统账户控制;
- 配置文件和环境变量只存在于当前设备;
- 外部攻击者无法直接访问服务端口。
本地 MCP 调用架构
flowchart LR
subgraph Local["本机信任边界"]
A[AI 客户端] --> B[本地 MCP Server]
B --> C[本地文件]
B --> D[本地数据库]
B --> E[系统工具]
end
在这个模式下,“能接触这台电脑”往往就代表“被允许使用这些工具”。对于单人开发环境,这种安全模型虽然不精细,但勉强成立。
一旦搬到服务器,情况完全不同。
flowchart LR
A[AI 客户端] --> B[公网端口]
B --> C[共享 Token]
C --> D[MCP Server]
D --> E[文件与数据库]
D --> F[系统命令]
D --> G[上游模型或第三方 API]
H[多人共用同一凭证] -.-> C
I[所有上游密钥集中保存] -.-> D
如果只是开放 HTTP/SSE 或 Streamable HTTP 接口,再在请求头里加一个所有人共用的长期 Token,那么系统实际上变成了:
谁拿到 Token,谁就拥有整个 MCP Server 的全部能力。它还要面对本地模式几乎不存在的问题:
- 公网扫描和自动化探测;
- Token 被复制到聊天记录或工单;
- 多人共用凭证,无法区分真实调用者;
- 代理、网关或日志意外记录请求头;
- 某个低权限客户端调用了高风险工具;
- 离职成员仍然持有长期有效密钥;
- MCP Server 直接暴露上游模型和第三方服务凭证。
因此,远程 MCP 的安全架构至少应该变成下面这样:
flowchart LR
A[AI 客户端] -->|HTTPS + 独立凭证| B[API 网关]
B --> C[身份认证]
C --> D[工具级与参数级授权]
D --> E[MCP Server]
E --> F[文件、数据库、系统工具]
E --> G[下游模型或第三方 API]
H[密钥存储系统] --> C
H --> E
B --> I[审计日志]
D --> I
E --> I
I --> J[检索与异常告警]
连得上只是功能问题,谁能调用什么、出了问题能否追查,才是安全问题。
---
第一个漏洞:认证了身份,却没有限制这个身份能做什么
MCP 远程化后,很多团队的第一反应是“加一个 Token”。
方向没错,但只完成了第一步。
这里有三个经常被混在一起的概念:
- 身份认证:确认你是谁;
- 权限控制:判断你能做什么;
- 请求上下文:记录这次操作代表谁发起、从哪个客户端发起。
如果所有人共享一个 Token,系统只能确认“请求携带了正确凭证”,却不知道背后是普通员工、管理员,还是自动化任务。
这就像公司安装了门禁,却给所有人发了一把能打开机房、财务室和仓库的万能钥匙。
远程请求必须携带可识别的身份凭证
curl https://example.com/mcp \
-H "Authorization: Bearer $MCP_ACCESS_TOKEN" \
-H "X-Request-ID: demo-20250308-001" \
-H "Content-Type: application/json" \
-d '{
"tool": "query_orders",
"arguments": {
"date": "2025-03-08"
}
}'
Authorization 用于认证,X-Request-ID 则用于把网关、MCP Server 和下游系统的日志串联起来。
但认证成功后,必须继续执行授权判断:
identity = authenticate(request)
authorize(
identity=identity,
action="mcp.tool.call",
resource="query_orders"
)
result = call_mcp_tool("query_orders", request.arguments)
权限至少要细化到 Tool
下面是一份可用于改造讨论的示例权限矩阵,不是生产事故统计:
| 身份 | 查询数据 | 写入数据 | 读取文件 | 删除文件 | 执行命令 | |---|---:|---:|---:|---:|---:| | 普通用户 | 允许 | 部分允许 | 指定目录 | 禁止 | 禁止 | | 管理员 | 允许 | 允许 | 指定目录 | 二次确认 | 白名单命令 | | 自动化服务 | 允许 | 指定接口 | 禁止 | 禁止 | 禁止 |这里最重要的不是表格本身,而是权限设计的颗粒度:
权限不应只控制“能否调用某个 MCP Server”,还应细化到具体 Tool、资源范围和危险参数。
例如,允许用户调用 read_file,不代表允许读取服务器上的任意路径:
if tool_name == "read_file":
ensure_path_in_allowlist(arguments["path"])
同样的逻辑也适用于其他工具:
- 数据库查询只能执行只读 SQL,不允许
DROP、DELETE或任意拼接; - 文件工具只能访问工作目录,不能读取
/etc、SSH 目录或密钥文件; - 命令工具只能执行白名单命令,不能把任意字符串交给 Shell;
- 消息工具只能向指定群组发送,不能任意选择外部联系人;
- 写入工具应限制目标资源、字段范围和单次操作规模。
还有一个容易忽略的细节:401 和 403 应该明确区分。
401 Unauthorized:凭证缺失、无效或已过期,系统不知道你是谁;403 Forbidden:身份已经确认,但没有权限执行这项操作。
这不只是接口规范问题,也能帮助运维快速判断:究竟是客户端配置错了,还是有人正在尝试越权。
---
第二个漏洞:密钥能生成,却不能轮换和撤销
本地单人使用时,把 Token 写在配置文件里似乎没有立即出问题。远程化以后,同样的做法会迅速放大风险。
常见错误包括:
- 长期密钥直接写进代码;
- Token 被提交到代码仓库;
- 密钥被打包进 Docker 镜像;
- 多名用户共享一个 Key;
- 在聊天记录里直接发送完整凭证;
- 前端环境变量保存服务端密钥;
- 启动参数、错误堆栈和访问日志输出 Token;
- 密钥泄漏后,只能停服并修改全部客户端配置。
最直接的错误示范是:
MCP_TOKEN = "sk-long-lived-secret"
最低限度的修正方式,是从运行环境读取:
import os
MCP_TOKEN = os.environ["MCP_ACCESS_TOKEN"]
但需要强调:环境变量只是最低限度方案,不是完整的密钥管理系统。
生产环境应优先使用专门的 Secret 管理工具或云厂商密钥服务,并避免密钥出现在镜像层、命令行参数、错误堆栈和普通业务日志中。
一套最小可行的密钥生命周期
1. 独立生成
不同用户、客户端和自动化服务使用不同凭证,禁止全员共享一个 Key。
2. 按调用方发放
凭证必须绑定明确身份、用途和权限,而不是仅仅绑定一个项目名称。
3. 只展示一次
创建后只在安全页面展示完整值,服务端尽量保存哈希或交由密钥管理系统托管。
4. 设置过期时间
长期不变的永久密钥意味着一次泄漏可能带来长期风险。具体采用何种有效期,应根据业务敏感度和客户端更新能力决定。
5. 支持轮换
轮换不是简单地再生成一个新 Key,而应包含完整迁移流程:
- 创建新凭证;
- 新旧凭证短暂并存;
- 客户端迁移到新凭证;
- 确认旧凭证不再被使用;
- 吊销旧凭证;
- 对旧凭证的后续请求触发告警。
6. 能够立即吊销
一旦发现泄漏,应只封禁受影响的调用方,而不是让整个 MCP 服务停机换钥匙。
三类密钥必须分层保存
远程 MCP 中至少会出现三类凭证:
- 用户或客户端访问 MCP 的 API Key;
- MCP Server 调用内部服务的服务间凭证;
- MCP Server 调用模型或第三方 API 的上游密钥。
这三类密钥不能混为一谈,更不能全部交给 MCP 客户端。
客户端只需要知道“如何访问 MCP”,不应该拿到模型 API Key、数据库密码或邮件服务密钥。否则 MCP 原本应该充当的隔离层,反而会变成密钥分发器。
以一组示例改造配置为例:
- 原方案:1 个共享长期 Key,可调用 8 个工具;
- 改造后:按普通用户、管理员、桌面客户端、自动化服务拆分为 4 类凭证;
- 删除文件、执行命令、发送外部消息这 3 类高风险工具单独授权;
- 所有凭证增加有效期、轮换和立即吊销能力;
- 上游模型与第三方 API 密钥只保存在服务端。
这些数字用于展示配置方法,不代表真实生产事故统计。
---
第三个漏洞:日志很多,却无法回答“到底是谁做的”
下面这种日志非常常见:
2025-03-08 10:20:30 POST /mcp 200
它记录了时间、接口和状态码,看起来“有日志”,但真正发生问题时几乎没有帮助。
它无法回答:
- 谁调用了删除工具?
- 请求来自哪个客户端?
- 删除的是哪个文件?
- 系统为什么允许这次操作?
- 用户是否进行了二次确认?
- 请求是否继续调用了外部系统?
- 同一次 AI 对话中还有哪些关联操作?
真正有价值的是结构化审计日志:
{
"timestamp": "2025-03-08T10:20:30Z",
"request_id": "demo-20250308-001",
"actor_id": "user_1024",
"client_id": "desktop_agent_03",
"tool": "delete_file",
"resource": "/workspace/tmp/report.csv",
"authorization": "allowed",
"confirmation": "approved",
"result": "success",
"duration_ms": 184,
"source_ip": "203.0.113.10"
}
一条最小可用的审计记录,至少应包含:
- 时间;
- Request ID 或 Trace ID;
- 真实调用者身份;
- 客户端或服务账号;
- Tool 名称;
- 资源对象;
- 参数摘要;
- 权限判断结果;
- 执行状态;
- 来源 IP。
对于删除文件、执行命令、数据库写入、发送邮件或外部消息等高风险操作,还应记录用户确认或审批信息。
日志不能成为第二个泄密源
审计日志不是记录得越多越好。
密码、完整 Token、Cookie、身份证号、完整提示词和文件正文,都不应未经处理地进入日志。尤其不要为了排查认证问题,直接输出完整的 Authorization 请求头。
正确的脱敏方式可以是:
{
"authorization_header": "[REDACTED]",
"api_key_suffix": "**7A2F"
}
日志系统还需要考虑四件事:
- 完整性:避免普通业务账户随意删除或修改审计记录;
- 留存周期:根据业务敏感度和合规要求设置;
- 异常告警:短时间大量失败、跨地域调用、频繁访问高风险工具应触发提醒;
- 链路关联:使用 Trace ID 串联 AI 对话、模型请求、MCP 调用和下游 API。
否则,即使日志字段足够丰富,也只是一堆无法拼起来的碎片。
---
从“加一个 Token”升级为完整安全闭环
重新配置后的请求流程应该是:
1. AI 客户端通过 HTTPS 携带独立凭证和 Request ID;
2. API 网关验证凭证是否有效、过期或已吊销;
3. 系统把凭证映射到具体用户或服务身份;
4. 权限模块判断该身份能否调用指定 Tool;
5. 参数规则继续检查文件路径、SQL 类型和命令范围;
6. 高风险操作要求二次确认;
7. MCP Server 使用服务端凭证访问下游系统;
8. 审计系统记录授权结果与执行结果;
9. 异常调用触发告警,必要时立即吊销凭证。
可以按三个层级逐步上线,不必第一天就搭建复杂的“零信任平台”。
最低标准
- 强制使用 TLS;
- 每个调用方使用独立 Key;
- 配置 Tool 白名单;
- 输出结构化审计日志;
- 对敏感字段进行脱敏。
团队标准
- 用户身份与服务身份分离;
- 按 Tool 和资源范围划分权限;
- 建立密钥过期、轮换和吊销流程;
- 对异常调用进行告警;
- 区分认证失败和权限不足。
生产标准
- 使用短期凭证;
- 实施参数级细粒度授权;
- 高风险操作二次确认;
- 集中检索和分析审计日志;
- 建立不可随意篡改的操作留痕;
- 准备凭证泄漏和越权调用的应急响应流程。
真正容易漏掉的不是某一个配置,而是三者之间的关联:身份必须映射到权限,密钥必须绑定身份,日志必须能还原该身份执行过的具体操作。
---
建议补齐的实机截图
发布复盘时,最好从自己的测试或生产环境补充以下截图,并对域名、IP、Token、用户名和业务数据打码:
- MCP 服务开放远程接口前后的配置差异;
- API Key 创建、有效期设置和吊销界面;
- 凭证无效返回
401、权限不足返回403的对比; - 按用户、Tool 和 Request ID 检索审计日志;
- 高风险工具触发二次确认的界面;
- 密钥误入配置文件或日志的反面示例。
不要使用无法验证来源的“事故截图”,也不要为了增强故事性伪造生产数据。
---
一页式 MCP 远程安全检查清单
- [ ] 是否强制使用 HTTPS
- [ ] 每个调用方是否使用独立凭证
- [ ] 是否区分认证失败与权限不足
- [ ] 是否按 Tool 设置权限
- [ ] 是否限制文件路径、SQL 类型和命令范围
- [ ] 密钥是否有过期、轮换和吊销机制
- [ ] 日志是否记录真实调用者和 Request ID
- [ ] 日志是否对 Token、Cookie 及敏感参数脱敏
- [ ] 高风险操作是否需要二次确认
- [ ] 是否能在 10 分钟内定位并封禁泄漏凭证
如果这 10 项中有多项无法确认,先不要急着增加更多 MCP 工具。工具越多,权限边界越复杂,共享凭证带来的影响范围也越大。
当 MCP 从本地工具变成远程服务后,上游模型 API 和下游工具接口通常也会逐渐增多。完成权限自查后,如果还需要梳理统一 API 接入、调用凭证和客户端配置,可以前往 api.884819.xyz 查看接入方式,再对照本文检查是否做到了凭证隔离、最小权限和调用留痕。
8848AI 平台内置 AI 对话功能,用户名和密码即可注册,不需要邮箱验证;国产模型如 Deepseek、千问等可免费使用,没有月租和订阅,按量付费。
新用户注册即送体验token。这次解决的是“谁能调用、密钥怎么管、出了问题怎么查”。但还有一个更棘手的问题:即使用户有权调用文件、数据库和命令行工具,怎样防止 AI 使用合法权限做出危险操作?
下一篇,我们将继续拆解 MCP 的工具白名单、参数级权限、路径沙箱和高风险操作二次确认——因为身份认证只是在门口验了证,真正决定安全性的,是进门之后还能碰什么。
本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。#MCP #AI安全 #权限管理 #密钥管理 #审计日志 #人工智能 #8848AI #AI教程