把 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,不允许 DROPDELETE 或任意拼接;
  • 文件工具只能访问工作目录,不能读取 /etc、SSH 目录或密钥文件;
  • 命令工具只能执行白名单命令,不能把任意字符串交给 Shell;
  • 消息工具只能向指定群组发送,不能任意选择外部联系人;
  • 写入工具应限制目标资源、字段范围和单次操作规模。

还有一个容易忽略的细节:401403 应该明确区分。

  • 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教程