跳转至

A2C-SMCP 安全性考虑

本文档描述实现 A2C-SMCP 协议时的安全性要求与建议。

传输安全

TLS 要求

部署环境 TLS 要求
公网部署 必须 使用 TLS
内网部署 推荐 使用 TLS
本地开发 可选

TLS 配置建议

# 推荐的 TLS 版本
TLS_VERSION = "TLSv1.2+"

# 推荐的密码套件
CIPHER_SUITES = [
    "TLS_AES_256_GCM_SHA384",
    "TLS_CHACHA20_POLY1305_SHA256",
    "TLS_AES_128_GCM_SHA256"
]

mTLS(双向 TLS)

对于需要端到端保密的场景,推荐采用 mTLS:

  • Client 和 Server 都需要证书
  • 提供双向身份验证
  • 适用于高安全要求的企业环境

认证与授权

认证机制

A2C-SMCP 通过 AuthenticationProvider 抽象认证:

class AuthenticationProvider(ABC):
    @abstractmethod
    async def authenticate(
        self,
        server: AsyncServer,
        environ: dict,
        auth: dict | None,
        headers: dict
    ) -> bool:
        """验证连接请求"""
        pass

认证策略

策略 适用场景 安全级别
API Key 简单场景、内部服务 中
JWT Token 用户认证、有时效要求 高
mTLS 服务间通信、高安全要求 最高

默认认证实现

SDK 提供默认认证实现 DefaultAuthenticationProvider:

auth = DefaultAuthenticationProvider(
    admin_secret="your_admin_secret",
    api_key_name="x-api-key"
)

注意: 默认实现仅适用于开发和测试,生产环境应实现自定义认证。

x-api-key 只是 SDK 默认值,不是协议要求

上方示例中的 api_key_name="x-api-key" 仅是 DefaultAuthenticationProvider 可配置参数的默认值,不是协议要求。部署方可以自由选择:

  • 凭据字段名:任意字符串。例如使用对下划线敏感的中间件(如默认配置的 nginx 会丢弃带下划线的请求头)的部署可用短横线 x-api-key,而使用自定义 API 网关的部署可用下划线 x_api_key。
  • 传输位置:Socket.IO 握手的 auth dict 或 HTTP 请求头——两者都被 AuthenticationProvider 接口签名 authenticate(server, environ, auth, headers) 同时接受。

因此 SDK 实现 MUST NOT 将任何具体字段名硬编码到凭据注入路径中,SHOULD 向调用方暴露可自由指定字段名与传输位置的 API。协议层对认证凭据的字段名与位置保持无感知,这是有意的设计。

凭证管理

零凭证传播原则

A2C-SMCP 的核心安全原则是零凭证传播:

┌──────────────────────────────────────────────────┐
│                    Agent                          │
│                                                   │
│  ✗ 不持有敏感凭证                                 │
│  ✗ 不接触 MCP Server Token                       │
│  ✗ 不访问本地资源                                 │
└──────────────────────────────────────────────────┘
                        │
                        │ SMCP 协议
                        ▼
┌──────────────────────────────────────────────────┐
│                   Computer                        │
│                                                   │
│  ✓ 持有 MCP Server 凭证                          │
│  ✓ 管理本地资源访问                              │
│  ✓ 凭证不外传                                    │
└──────────────────────────────────────────────────┘

凭证存储建议

凭证类型 存储位置 加密要求
API Key 环境变量 / 密钥管理服务 推荐
数据库密码 密钥管理服务 必须
证书私钥 文件系统(受保护) 推荐

环境变量安全

# 推荐:使用环境变量
export API_KEY="your_api_key"

# 不推荐:硬编码
api_key = "your_api_key"  # 危险!

MCP 上游授权(OAuth 2.0 等)

MCP 协议自身支持 OAuth 2.0 等授权机制以访问工具服务器的受保护资源。A2C-SMCP 不在协议层介入该握手过程——这是"零凭证传播原则"在 MCP 集成上的直接推论。

边界划分

┌───────────────┐        ┌───────────────┐        ┌──────────────────┐
│     Agent     │◀──────▶│   Computer    │◀──────▶│   MCP Server     │
│               │  SMCP  │               │  MCP   │  (受保护资源)     │
│ ✗ 不参与授权  │        │ ✓ 执行 OAuth  │        │                  │
│ ✗ 不持有 Token│        │ ✓ 存储 Token  │        │                  │
│               │        │ ✓ 负责刷新    │        │                  │
└───────────────┘        └───────────────┘        └──────────────────┘

Computer 实现要求

要求 说明
本地完成握手 OAuth 流程(Authorization Code、Device Code 等)必须在 Computer 宿主环境内完成,不通过 A2C-SMCP 信道承载授权参数
凭证不外传 Access Token、Refresh Token、client_secret、code_verifier 等禁止经由 A2C-SMCP 协议传输
用户引导本地化 需要用户完成浏览器授权或显示设备码时,应在 Computer 所在宿主环境呈现,不应通过 Agent 侧 UI 中转敏感 URL 参数
刷新透明 Token 过期由 Computer 自动刷新;若刷新失败,返回 4007 Tool Authorization Failed(见 error-handling.md)
授权缺失显式告知 工具首次调用前未完成授权时,返回 4006 Tool Authorization Required,不要泛化为一般工具执行错误

授权失败的硬约束(详见 error-handling.md → MCP 上游授权错误响应):Computer 一旦判定上游失败属授权类——含 MCP 客户端库把授权失败吸收为语义错误变体而丢状态码的情形(如 rmcp AuthRequired)——MUST 映射为 4006/4007、MUST NOT 降级为 4003 亦不得返回缺 meta.error_code 的通用失败结果(降级语义);且 MUST NOT 表现为调用挂起至 Agent 超时(可观测判据)。检测点归 SDK 自治,但"是否产出 4006/4007"与"是否挂起"是对端可感的硬约束。真实传输 conformance 向量见 fixtures/auth_error_conformance_vectors.json。

Agent 行为约束

  • 禁止在调用参数中尝试注入 Token、授权码或任何 OAuth 字段——这些字段即使被传输也不会被 Computer 信任。
  • 收到 4006/4007 时,不应自动重试,应将 Computer 返回的 auth_hint.message(非敏感文案)透传给上层。
  • 授权完成的判定由 Computer 负责;Agent 仅在用户显式重试后再次发起 client:tool_call,不得基于启发式(如等待固定时间)自动重试。

工具元数据提示(建议,非强制)

Computer 在向 Agent 暴露工具列表时,建议对需要授权的工具附加非敏感标注,便于 Agent 判断调用前置条件:

{
    "name": "github_create_issue",
    "description": "...",
    "requires_auth": True,        # 建议字段
    "auth_provider": "github"     # 建议字段,仅用于展示,禁止包含 scope 之外的敏感细节
}

该字段在当前版本中不是协议强制字段,Agent 不应依赖其存在;Computer SDK 可根据实现粒度自行决定是否暴露。后续版本会评估将其纳入 tool 数据结构。

房间隔离

隔离要求

Server 必须实现以下隔离保障:

  1. 跨房间访问禁止: 事件不能路由到其他房间
  2. Agent 独占性: 一个房间只能有一个 Agent
  3. 消息隔离: 通知只广播给同一房间成员

实现检查清单

# Server 必须验证
def validate_room_access(sid, target_office_id):
    session = get_session(sid)
    if session.office_id != target_office_id:
        raise PermissionError("Cross-room access denied")

输入验证

数据验证要求

所有输入数据必须进行验证:

验证项 说明
类型检查 确保字段类型正确
长度限制 防止缓冲区溢出
格式校验 验证 URL、ID 等格式
范围检查 验证数值在合理范围内

工具参数验证

# 推荐:使用 Pydantic 进行验证
from pydantic import BaseModel, validator

class ToolCallParams(BaseModel):
    tool_name: str
    params: dict
    timeout: int

    @validator('timeout')
    def validate_timeout(cls, v):
        if v <= 0 or v > 3600:
            raise ValueError('Timeout must be between 1 and 3600')
        return v

资源限制

DoS 防护

实现应该对以下资源设置限制:

资源 建议限制
并发连接数 每 IP 100 个
请求频率 每秒 100 请求
消息大小 1MB
工具调用超时 300 秒

限流实现

# 示例:使用令牌桶算法
from ratelimit import limits, sleep_and_retry

@sleep_and_retry
@limits(calls=100, period=1)
def handle_request(request):
    # 处理请求
    pass

写入通道安全(Landing 沙箱)

client:put_blob 的上行写入落盘能力,安全边界如下(完整契约见 通用二进制传输 §3 / §7):

沙箱边界(fail-closed)

  • 落点由 Computer 决断:Agent 只能把字节写进 Computer 显式授权的 landing root,不是任意文件写原语
  • name_hint 中的穿越组件 MUST 被消毒剥离或忽略(Computer 自定安全名),落点构造上严格落于 root 内;landing root 未配置 / 不可写 → 4019 forbidden,零字节落盘
  • integrity(sha256 不符)/ too_large(声明超限)路径下零产物:.part 丢弃、无部分文件可见

受信配置与 GC 边界

  • landingRoot 仅受信 scope(user / local / flag / policy / embed)可设;project scope 提供该键 MUST 被拒绝——project settings 入 git 随仓库分发,clone 的仓库不得把写目标重定向到任意路径(computer-management §7 不变量 #6)
  • 上传会话有界 MUST:闲置超时 + 并发上限 + 孤儿 .part 回收(阈值 SDK 自治),防无界会话 DoS
  • GC 严格限于 landing root 内(canonicalize + realpath 围栏),不越授权边界(computer-management §7 不变量 #5)

路径披露权衡

PutBlobRet.landing_path 为绝对路径,会暴露 landing root 的位置(含 OS 用户名级别信息)。此披露为有意设计:不构成凭据边界(拥有本地 Bash 类工具的 Agent 本就能自行发现),且不带来新能力(写任意路径仍被 put_blob 原语锁死);换取的是工具参数直接可用、Agent 感知不到沙箱存在。

日志与审计

安全日志

应该记录以下安全相关事件:

事件类型 日志级别
认证失败 WARNING
跨房间访问尝试 WARNING
权限拒绝 WARNING
异常高频请求 WARNING
敏感操作 INFO

日志格式

# 推荐的安全日志格式
logger.warning(
    "Authentication failed",
    extra={
        "client_ip": client_ip,
        "user_agent": user_agent,
        "reason": "Invalid API key",
        "timestamp": datetime.utcnow().isoformat()
    }
)

敏感信息过滤

日志禁止包含:

  • API 密钥或 Token
  • 密码
  • 个人身份信息(PII)
  • 信用卡号等金融信息

安全更新

依赖管理

  • 定期更新依赖包
  • 使用 pip-audit 或 safety 检查漏洞
  • 订阅安全公告

漏洞响应

  1. 评估漏洞影响
  2. 制定修复计划
  3. 发布安全补丁
  4. 通知用户更新

安全检查清单

部署前检查

  • TLS 已启用(公网部署)
  • 认证机制已配置
  • 敏感凭证未硬编码
  • 输入验证已实现
  • 资源限制已配置
  • 安全日志已启用
  • 依赖无已知漏洞

定期检查

  • 证书有效性
  • API 密钥轮换
  • 日志审计
  • 依赖更新
  • 渗透测试

参考

  • OWASP Top 10: https://owasp.org/www-project-top-ten/
  • Socket.IO 安全: https://socket.io/docs/v4/security/
  • Python 安全: https://python-security.readthedocs.io/