一、凭证与密钥管理
凭证泄露是MCP服务器最常见的安全风险之一。许多开发者在配置文件中硬编码API Key、数据库密码等敏感信息,一旦配置文件被意外提交到版本控制系统,将造成严重的安全事故。正确的凭证管理策略应当贯穿MCP服务器的整个生命周期。
避免硬编码密钥
不要在 claude.json 或任何配置文件中直接写入API Key、Token或密码。使用环境变量或密钥管理服务动态注入凭证。
环境变量传递
通过系统环境变量或 .env 文件传递敏感信息,MCP服务器在运行时从环境变量中读取凭证。
Git忽略配置
在 .gitignore 中明确排除所有可能包含密钥的配置文件,包括 .env、*.key、config.json 等。
定期轮换Token
为MCP服务器使用的所有API Token设置定期轮换策略,降低单个Token泄露带来的影响范围。
推荐做法 - 使用环境变量配置MCP服务器凭证:
# .env 文件(已加入 .gitignore)
MCP_DATABASE_URL=postgresql://user:pass@localhost:5432/mcpdb
MCP_OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
MCP_SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxx
# claude.json 中引用环境变量
{
"mcpServers": {
"database": {
"command": "node",
"args": ["server.js"],
"env": {
"DATABASE_URL": "${MCP_DATABASE_URL}",
"API_KEY": "${MCP_OPENAI_API_KEY}"
}
}
}
}
常见风险:将包含API Key的配置文件提交到公开或内部Git仓库。即使仓库是私有的,也应避免硬编码密钥,因为所有有权访问仓库的人员都可能获取这些凭证。建议使用 git-secrets 或类似工具在提交前扫描密钥。
最佳实践:对于生产环境,建议使用专业的密钥管理服务如 AWS Secrets Manager、HashiCorp Vault 或 Azure Key Vault。MCP服务器启动时通过SDK从密钥管理服务获取凭证,确保密钥不在文件系统中持久化存储。
二、路径穿越防护
Filesystem等MCP服务器允许AI助手访问本地文件系统,如果不对文件路径做充分校验,攻击者可能通过路径穿越(Path Traversal)技术访问预期范围之外的文件。路径穿越攻击通常利用 ../ 等特殊字符绕过目录限制,读取系统敏感文件。
点击复制
路径穿越攻击示例:
用户输入: ../../etc/passwd
预期访问: /data/user1/../../etc/passwd = /etc/passwd
风险: 读取系统密码文件
防护后:
白名单目录: /data, /home/user
规范化路径: /data/user1/docs/../../../etc/passwd -> /etc/passwd (拒绝访问)
核心防护措施
- 路径白名单机制:在MCP服务器配置中明确定义允许访问的根目录列表,所有文件操作必须落在白名单目录范围内
- 路径规范化:使用 path.resolve() 或 realpath() 将用户输入的路径解析为绝对路径,移除所有的 .. 和 . 以及符号链接
- 前缀校验:将规范化后的路径与白名单目录进行前缀匹配,确保目标路径以允许的目录前缀开头
- 禁止特殊路径:主动拦截包含 .. 、~ 、符号链接或空字节(%00)的路径输入
Node.js 路径安全校验示例:
const path = require('path');
const ALLOWED_DIRS = [
path.resolve('/data/mcp'),
path.resolve('/home/user/mcp-data')
];
function isPathSafe(userInput) {
// 拒绝包含路径穿越模式的输入
if (userInput.includes('..') || userInput.includes('~')) {
return false;
}
// 规范化路径
const resolved = path.resolve(userInput);
// 检查是否在白名单目录内
return ALLOWED_DIRS.some(allowed =>
resolved.startsWith(allowed + path.sep) || resolved === allowed
);
}
特别注意:仅仅过滤 ../ 是不够的。攻击者可以使用URL编码(%2e%2e%2f)、Unicode字符、符号链接跳转、NTFS流(::$DATA)等多种方式绕过简单的字符串过滤。必须使用路径规范化函数而非字符串匹配来进行安全校验。
三、命令注入防御
许多MCP服务器需要执行系统命令来完成特定任务,例如代码执行、文件操作或调用外部工具。如果用户输入未经充分校验直接拼接到命令中,攻击者可以利用shell注入技术执行任意命令,获取服务器的完全控制权。
参数化命令
使用 child_process.execFile 或 spawn 替代 exec,避免将参数经过shell解析。execFile直接创建子进程执行文件,不启动shell。
输入严格校验
对用户输入执行白名单校验模式,只允许已知安全的字符集(如字母、数字、特定符号),拒绝所有其他输入。
限制命令范围
定义可执行命令的白名单,不允许用户指定任意命令或可执行文件路径。只暴露有限的预定义操作接口。
沙箱执行
在隔离环境(Docker容器、gVisor、Firecracker微VM)中执行用户触发的命令,即使发生注入也限制其影响范围。
不安全做法 - 字符串拼接命令:
// 危险!用户可以在 name 中注入 ; rm -rf / 等恶意命令
const result = execSync(`git log --oneline -n 5 --author="${userInput}"`);
安全做法 - 参数化执行:
// 使用 spawn 传递参数数组,避免shell注入
const { spawn } = require('child_process');
const child = spawn('git', [
'log', '--oneline', '-n', '5',
'--author', userInput
], { shell: false });
点击复制
命令注入攻击的本质原因:
将不可信数据传递给shell解释器(如/bin/sh -c),
shell会解析其中的特殊字符(;、|、&&、$()、``等)。
安全原则:永远避免不可信数据经过shell解析流程。
使用 execFile / spawn 时设置 shell: false 是最基本的防护。
进阶防护:对于需要复杂命令执行的MCP服务器,建议使用专用的命令解析库(如Python的shlex.quote),将所有参数进行转义处理。同时设置操作系统级别的命令执行权限,以低权限用户运行MCP服务器进程。在Docker环境中,可以考虑使用 seccomp 和 AppArmor 限制系统调用。
四、权限最小化
权限最小化原则(Principle of Least Privilege)是信息安全的核心原则之一,在MCP服务器的开发和部署中尤为重要。MCP服务器在运行时应当仅持有完成任务所需的最小权限集,任何超出必要范围的权限都应被移除。
应用场景
数据库权限最小化示例:
-- 避免使用 root 或管理员账户连接数据库
-- 错误做法:GRANT ALL PRIVILEGES ON *.* TO 'mcp_user'@'%'
-- 正确做法:仅授予查询和特定操作的权限
CREATE USER 'mcp_reader'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT ON mcp_database.articles TO 'mcp_reader'@'localhost';
GRANT SELECT, INSERT ON mcp_database.access_log TO 'mcp_reader'@'localhost';
-- 如果不需要写入,授予只读权限即可
- 数据库访问:MCP服务器使用独立的数据库账户,仅授予该服务器功能所需的最小权限。只读型MCP服务器应使用只读账户,需要写入操作的服务器应限制可操作的表范围
- API Token作用域:为MCP服务器申请的API Token应设置最小scope。例如,Slack机器人仅授权需要的频道读取权限,不需要admin权限
- 文件系统权限:MCP服务器进程以独立用户身份运行,该用户仅对必要目录拥有读写权限。使用操作系统文件权限(chmod/chown)和ACL限制访问范围
- 定期审计:每季度审查一次MCP服务器所使用的所有权限和凭证,撤销不再需要的权限,更新过期的Token
典型事故:某团队开发了一个文件搜索MCP服务器,最初仅需要读取用户目录下的文档,但开发阶段为了方便使用了管理员Token和root权限运行。上线后该服务器被注入攻击,攻击者利用其管理员权限删除了整个数据库。如果遵循权限最小化原则,即使被攻击,影响范围也会被限制在用户文档目录内。
实施建议:在Docker中运行MCP服务器时,使用非root用户运行进程,设置只读的文件系统层(readonly rootfs),并使用Docker的cap_drop功能移除所有不必要的Linux capabilities。在Kubernetes中,配置Pod Security Context强制执行这些限制。
五、SSRF防护
服务端请求伪造(Server-Side Request Forgery,SSRF)是MCP服务器的Fetch、Webhook等HTTP请求类服务器面临的主要安全威胁。攻击者可能利用MCP服务器的HTTP请求能力,访问内网中的敏感服务(如云服务元数据端点、内部数据库管理接口等)。
点击复制
SSRF攻击典型场景:
1. 云环境元数据攻击
用户输入: http://169.254.169.254/latest/meta-data/
目标: 获取云服务器IAM临时凭证(AWS/GCP/Azure)
2. 内网服务探测
用户输入: http://10.0.0.1:6379/ (Redis)
用户输入: http://localhost:9200/ (Elasticsearch)
目标: 探测并攻击内网未授权服务
3. 端口扫描
通过访问时间和响应内容判断内网端口开放情况
SSRF防护策略
- IP范围黑名单:禁止向私有IP地址段(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)以及云元数据IP(169.254.169.254、100.100.100.200)发起请求
- URL白名单:严格模式下,只允许向预定义的域名白名单发起HTTP请求,拒绝所有其他目标
- DNS重绑定防护:对域名进行两次DNS解析(首次检查时和连接目标时),如果解析结果不一致则拒绝请求,防止攻击者使用DNS重绑定绕过IP黑名单
- 协议限制:限制仅允许HTTP/HTTPS协议,禁止 file://、gopher://、dict:// 等可以访问本地文件系统的协议
Python SSRF防护示例:
import ipaddress
from urllib.parse import urlparse
import socket
PRIVATE_RANGES = [
ipaddress.ip_network('127.0.0.0/8'),
ipaddress.ip_network('10.0.0.0/8'),
ipaddress.ip_network('172.16.0.0/12'),
ipaddress.ip_network('192.168.0.0/16'),
ipaddress.ip_network('169.254.0.0/16'),
]
def validate_url(target_url):
parsed = urlparse(target_url)
# 协议限制
if parsed.scheme not in ('https', 'http'):
raise ValueError('仅允许HTTP/HTTPS协议')
# DNS解析并检查IP
ip = socket.gethostbyname(parsed.hostname)
addr = ipaddress.ip_address(ip)
if any(addr in network for network in PRIVATE_RANGES):
raise ValueError('禁止访问内网地址')
return True
容易被忽视的攻击面:URL重定向。MCP服务器在访问A.com时,如果A.com返回302重定向到169.254.169.254,仅仅检查原始URL是不够的。必须禁用自动重定向跟随,或者对每个重定向目标也执行相同的安全检查。
六、数据加密与传输安全
MCP服务器与AI助手客户端之间的通信可能包含敏感数据,例如用户查询中的个人信息、数据库查询结果中的业务数据、API调用中的认证凭证等。确保数据传输过程中的机密性和完整性是MCP服务器安全的基础要求。
TLS/HTTPS加密
使用SSE(Server-Sent Events)传输方式的MCP服务器必须配置TLS证书,通过HTTPS提供服务,防止中间人攻击。
数据库连接加密
MCP服务器与数据库之间的连接应使用TLS加密(如PostgreSQL的SSL mode、MongoDB的TLS),避免明文传输。
存储加密
缓存到本地的敏感数据应使用AES-256等算法加密存储。密钥通过环境变量或密钥管理服务获取,不硬编码。
证书验证
MCP服务器在发起对外HTTP请求时,应验证目标服务器的TLS证书有效性,不关闭证书验证(rejectUnauthorized: false)。
SSE传输的安全配置示例(Node.js Express + TLS):
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
// 配置TLS证书
const options = {
key: fs.readFileSync('/etc/ssl/private/mcp-server.key'),
cert: fs.readFileSync('/etc/ssl/certs/mcp-server.crt'),
// 使用现代TLS版本和强加密套件
minVersion: 'TLSv1.2',
ciphers: [
'TLS_AES_128_GCM_SHA256',
'TLS_AES_256_GCM_SHA384',
'ECDHE-RSA-AES128-GCM-SHA256',
].join(':'),
};
// SSE端点
app.get('/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
// 发送MCP事件...
});
https.createServer(options, app).listen(3443);
网络安全配置建议:
- 将MCP服务器部署在内网或VPN环境中,不直接暴露在公网
- 如果必须公网暴露,使用反向代理(Nginx、Caddy)统一管理TLS终止
- 配置防火墙规则,仅允许AI助手客户端的IP范围访问MCP服务器
- 使用API Gateway或认证中间件对请求进行身份验证
七、审计与监控
安全审计与监控是MCP服务器安全保障的最后一道防线。即使前面所有的防护措施都到位,仍然可能出现零日漏洞或配置错误导致的意外。完善的日志记录和监控机制可以帮助及时发现异常行为并快速响应安全事件。
点击复制
审计日志应当记录的信息(不包含敏感数据):
1. 操作时间戳 (timestamp)
2. 操作类型 (action: read_file / execute_query / http_request)
3. 操作目标 (target: 文件名 / SQL语句前50字符 / URL域名)
4. 请求来源 (source: 客户端ID或IP)
5. 操作结果 (result: success / denied / error)
6. 执行耗时 (duration_ms)
不应记录的内容:完整SQL语句、文件内容、API Key、密码等敏感信息
监控实施要点
- 结构化日志:使用JSON格式记录所有操作日志,便于后续分析和导入日志管理平台(ELK、Loki、Datadog等)
- 异常检测:设置监控规则检测异常操作模式,如短时间内大量文件读取、频繁访问系统敏感路径、异常时间段的活动等
- 频率限制:为每个客户端设置操作频率限制(Rate Limiting)和配额管理(Quota),防止MCP服务器被滥用
- 告警通知:当检测到安全事件时通过邮件、Slack、飞书等多渠道发送告警通知,确保安全团队能够及时响应
- 定期审查:每周审查操作日志摘要,识别潜在的安全问题和优化机会,建立安全事件的复盘机制
结构化审计日志示例(JSON格式):
{
"timestamp": "2026-05-08T09:21:38.123Z",
"mcp_server": "database-query",
"client_id": "claude-desktop-user-001",
"action": "query",
"target": "SELECT COUNT(*) FROM users WHERE ...",
"target_truncated": true,
"result": "success",
"rows_returned": 1,
"duration_ms": 45,
"rate_limit_remaining": 95
}
日志安全注意事项:审计日志本身包含敏感信息,需要妥善保护。日志文件应设置严格的访问权限,传输到集中日志平台时应加密。日志保留周期根据合规要求设定(通常为90天到1年),过期日志应当安全删除。避免在日志中记录个人身份信息(PII)或认证凭证。
生产环境检查清单:
1. 是否所有MCP服务器操作都已记录日志?
2. 日志是否不包含敏感数据?
3. 是否设置了操作频率限制(每秒/每分钟/每小时)?
4. 是否配置了异常操作告警?
5. 是否有日志定期审查的流程?
6. 日志存储是否满足合规要求?