最近看到一份安全审计报告,有人对763个MCP服务器做了安全扫描,结果发现31%的服务器存在可利用的安全漏洞。
这个数字让我倒吸一口凉气。
MCP(Model Context Protocol)是Anthropic推出的协议,让AI模型可以调用外部工具和数据源。听起来很酷对吧?但问题是——当AI模型能调用外部工具时,这些工具本身的漏洞就变成了AI系统的漏洞。
我自己也用过不少MCP服务器,看到这个报告后赶紧去检查了一下自己的环境。这篇文章把MCP安全的问题、原理和防护方案整理出来,希望对大家有用。
什么是MCP?为什么它有安全问题?
MCP的工作原理(简化版)
# MCP的工作流程:
#
# 你的AI助手(比如Claude Desktop)
# ↓ 调用
# MCP Client(内置在AI工具中)
# ↓ 协议通信
# MCP Server(一个独立的服务进程)
# ↓ 执行操作
# 实际操作(读写文件、查数据库、调API等)
#
# 举例:
# 你问Claude:"帮我查一下客户数据库里张三的信息"
# Claude通过MCP调用数据库查询服务器
# 服务器执行SQL查询并返回结果
为什么MCP服务器容易有安全问题
1. MCP服务器通常有较高的系统权限
为了"方便",很多MCP服务器被赋予了过高的权限。比如文件系统MCP服务器,你给它配了整个home目录的读写权限——这意味着通过这个MCP服务器,AI模型可以读写你电脑上的任何文件。
2. 缺乏统一的安全标准
MCP是一个相对较新的协议,安全最佳实践还没有形成共识。很多开发者写MCP服务器时,主要考虑的是"功能能不能用",而不是"安全不安全"。
3. 输入验证不足
AI模型生成的调用参数直接传给MCP服务器执行。如果服务器没有做好输入验证,就可能被注入恶意指令。
扫描发现的漏洞类型
根据安全审计报告,763个MCP服务器中主要存在以下几类漏洞:
1. Schema验证缺失(最常见)
// 问题示例:MCP服务器没有验证输入参数
// 服务器定义了一个文件读取工具:
{
"name": "read_file",
"description": "读取文件内容",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string"
}
}
// 问题:没有限制path的范围!
// 攻击者可以让AI传入 "/etc/passwd" 或 "/etc/shadow"
}
}
// 安全的写法应该是:
{
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"pattern": "^/safe/directory/.*" // 限制只能访问安全目录
}
},
"required": ["path"]
}
}
我的思考:这个问题太常见了。我之前写MCP服务器的时候也没注意——"反正只是自己用,能跑就行"。但问题是,AI模型可能会被提示注入攻击(Prompt Injection),生成恶意的工具调用参数。如果你的MCP服务器没有验证输入,那攻击者就可以通过AI模型来操作你的系统。
2. 路径遍历漏洞
# 问题代码示例
import os
def read_file(path):
# 直接拼接路径,没有检查是否跳出了允许的目录
full_path = os.path.join(BASE_DIR, path)
# 攻击者可以传入 "../../etc/passwd"
# 实际路径变成:/safe/dir/../../etc/passwd → /etc/passwd
with open(full_path, 'r') as f:
return f.read()
# 安全的修复:
def safe_read_file(path):
full_path = os.path.realpath(os.path.join(BASE_DIR, path))
# 检查解析后的真实路径是否仍在允许的目录下
if not full_path.startswith(os.path.realpath(BASE_DIR)):
raise SecurityError("路径越界!")
with open(full_path, 'r') as f:
return f.read()
3. 未授权的命令执行
这是最危险的一类漏洞。很多MCP服务器提供"执行命令"功能,但没有限制可以执行什么命令:
# 危险示例:不限制命令执行
def execute_command(command):
import subprocess
# 直接执行任意命令!
result = subprocess.run(command, shell=True, capture_output=True)
return result.stdout.decode()
# 安全的修复:命令白名单 + 参数过滤
import shlex
ALLOWED_COMMANDS = {
'ls': {'args': ['-la', '-l']},
'cat': {'args': []},
'grep': {'args': ['-i', '-n']},
}
def safe_execute_command(command_str):
parts = shlex.split(command_str)
cmd = parts[0]
if cmd not in ALLOWED_COMMANDS:
raise SecurityError(f"不允许执行命令: {cmd}")
# 只允许预定义的参数
for arg in parts[1:]:
if cmd in ALLOWED_COMMANDS and ALLOWED_COMMANDS[cmd]['args']:
if arg not in ALLOWED_COMMANDS[cmd]['args']:
raise SecurityError(f"不允许的参数: {arg}")
result = subprocess.run(parts, capture_output=True, text=True)
return result.stdout
4. 敏感信息泄露
# 问题:MCP服务器在日志或错误信息中泄露敏感数据
#
# 比如数据库MCP服务器的错误信息:
# "连接失败:用户名 admin,密码 P@ssw0rd123,主机 db.company.com:5432"
#
# 这些信息会返回给AI模型,进而可能出现在对话中
#
# 安全做法:
# - 错误信息中不要包含连接凭据
# - 日志脱敏
# - 敏感配置不要硬编码在MCP服务器代码中
我是怎么检查自己的MCP环境的
检查清单
# 1. 列出你安装的所有MCP服务器
# Claude Desktop的配置通常在:
cat ~/.config/claude/claude_desktop_config.json
# 或
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
# 2. 检查每个MCP服务器的权限范围
# 问自己:
# - 这个服务器能访问哪些目录/资源?
# - 它有没有执行命令的能力?
# - 它连接了哪些外部服务?
# 3. 检查是否有已知漏洞
# 关注MCP服务器的GitHub仓库的issues和security标签
我自己的踩坑经历
经历1:文件MCP服务器的权限过大
我之前配置了一个文件系统的MCP服务器,给它配了整个home目录的权限。后来有一次让AI帮我整理文件,结果AI模型在对话中泄露了我.ssh目录下的文件名列表。
虽然没有泄露私钥内容,但这已经足够让人心惊了——如果AI模型被提示注入攻击劫持,它完全可以通过MCP服务器读取你的SSH私钥。
修复:把MCP服务器的可访问目录限制到了工作项目目录。
经历2:数据库MCP服务器没有限制操作类型
我有一个连接PostgreSQL的MCP服务器,最初配置时没有限制操作类型。也就是说,AI模型不仅能SELECT,还能DROP TABLE。
后来我意识到这个问题,马上加了限制:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://readonly_user:***@localhost:5432/mydb"
}
}
}
}
关键点:
- 使用只读用户连接数据库
- 该用户只有SELECT权限
- 即使AI被攻击,也无法修改或删除数据
安全使用MCP的实践指南
原则一:最小权限
// 不要这样做:
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"],
"env": {
"ALLOWED_DIRECTORIES": "/home/user" // 整个home目录!
}
}
// 应该这样做:
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"],
"env": {
"ALLOWED_DIRECTORIES": "/home/user/projects" // 只开放项目目录
}
}
原则二:只读优先
# 数据库连接用只读账号
CREATE USER ai_reader WITH PASSWORD '***';
GRANT CONNECT ON DATABASE mydb TO ai_reader;
GRANT USAGE ON SCHEMA public TO ai_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ai_reader;
# 文件系统只给读权限,不要给写权限
# 除非你非常确定需要写操作
原则三:网络隔离
# 如果MCP服务器需要连接外部API,
# 确保它只能访问必要的端点
# 使用防火墙规则限制:
# 只允许MCP进程访问特定的IP和端口
iptables -A OUTPUT -m owner --uid-owner mcp_user \
-d api.example.com -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner mcp_user -j DROP
原则四:监控和日志
# 在MCP服务器中加入操作日志
import logging
from datetime import datetime
logging.basicConfig(
filename='/var/log/mcp_audit.log',
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
def log_mcp_operation(tool_name, args, result):
"""记录每次MCP工具调用"""
logging.info(
f"Tool: {tool_name} | "
f"Args: {args} | "
f"Result length: {len(str(result))} | "
f"Time: {datetime.now()}"
)
给不同角色的建议
给AI工具开发者
- MCP服务器的输入验证是必须的,不是可选的
- 默认最小权限,让用户自己开放更多权限
- 所有MCP服务器都应该有操作日志
- 定期做安全审计,关注社区的安全公告
给系统管理员
- 审查现有的MCP服务器配置,检查权限是否过大
- 为MCP服务器创建专用的低权限用户
- 网络层面隔离MCP服务器,限制其能访问的资源
- 设置监控告警,异常的MCP操作要能及时发现
给普通AI工具用户
- 不要随便安装来路不明的MCP服务器
- 配置MCP服务器时,权限给到"刚好够用"就行
- 敏感数据不要放在MCP服务器能访问的目录里
- 定期检查你的MCP配置,不需要的服务器及时关掉
总结
- 31%的MCP服务器有漏洞——这不是危言耸听,这是实际扫描结果
- 最常见的是Schema验证缺失和路径遍历——技术门槛不高,但影响很大
- 最小权限是核心原则——给MCP服务器的权限越小越好
- AI工具的安全不能只靠AI本身——底层基础设施的安全同样重要
最后说一句:MCP是一个很好的协议,它让AI变得更加强大和灵活。但强大意味着危险也更大。就像你给一个实习生开了公司数据库的管理员权限——不是不能开,而是你得确保他知道什么该做什么不该做。
AI也是一样——给它工具之前,先想好安全边界。