安全说明
安全架构概览
flowchart LR
Client["客户端请求"] --> CORS["CORS 中间件<br/>来源校验"]
CORS --> JWT["JWT 中间件<br/>令牌验证(白名单跳过)"]
JWT --> Router["路由处理器"]
Router --> Access["设备访问控制<br/>can_access() 权限检查"]
Access --> Proxy["设备转发<br/>WebSocket 加密传输"]
身份认证
JWT 令牌
使用 PyJWT + HS256 对用户身份进行签名。
- 密钥通过
CLOUD_JWT_SECRET配置,生产环境要求至少 32 字符 - 令牌有效期默认 72 小时,可通过
CLOUD_JWT_EXPIRE_HOURS调整 - 客户端通过
Authorization: Bearer <token>头部传递令牌
用户管理
用户通过 CLOUD_AUTH_USERS 环境变量以 JSON 格式配置:
{
"admin": {
"password": "strong-password",
"role": "admin",
"display_name": "系统管理员"
},
"operator": {
"password": "operator-password",
"role": "user",
"display_name": "操作员"
}
}
设备认证
设备通过预设的共享 Token 认证:
- 建立 WebSocket 连接,携带
X-Device-ID头部 - 发送
register帧,包含token字段 - 服务端与
CLOUD_DEVICE_TOKEN比对,匹配后标记设备为已注册 - 未通过认证的设备无法接收代理请求
设备绑定与权限隔离
- 非管理员用户只能访问绑定的设备
- 管理员(
role == "admin")可访问所有设备 - 绑定关系持久化在
device_bindings.json文件中
生产配置校验
config.validate_production_config() 在 CLOUD_ENV=production 时执行:
| 检查项 | 失败条件 |
|---|---|
| JWT 密钥强度 | 使用默认值或长度 < 32 字符 |
| 设备 Token | 使用默认值或以 replace-with- 开头 |
| 管理员密码 | 使用默认值或以 replace-with- 开头 |
全部通过后服务启动;否则抛出 RuntimeError 拒绝启动。
CORS 策略
config.CORS_ORIGINS 明确定义允许的跨域来源。生产环境中不应包含通配符或不可信来源。
固件安全
下位机固件采用 ECDSA P-256 + SHA-256 签名链保护:
- 固件在受控构建环境签名(私钥不进入 API 服务器)
- 上传后服务端验证镜像头结构、摘要和 CRC
- 版本发布和激活全程记录审计日志
通信安全
| 层次 | 协议 | 安全措施 |
|---|---|---|
| 客户端 → Nginx | HTTPS | TLS 1.3,由 Nginx 终止 |
| Nginx → Cloud API | HTTP | 内网通信,127.0.0.1 |
| 设备 → Cloud API | WSS | WebSocket over TLS |
| 设备请求代理 | JSON/WS | 设备 Token 认证 |
威胁模型
| 威胁 | 缓解措施 |
|---|---|
| 未授权 API 访问 | JWT 中间件 + 路由白名单 |
| 设备仿冒 | 共享 Token + 设备 ID |
| 跨域攻击 | 严格 CORS 白名单 |
| JWT 伪造 | HS256 签名 + 生产密钥强度校验 |
| 固件篡改 | ECDSA 签名 + SHA-256 摘要校验 |
| 配置泄露 | 环境变量 + .gitignore 排除 .env |
密钥管理建议
- 生成强随机密钥:
# PowerShell 生成 64 字符随机 Token
$rand = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 64 | ForEach-Object {[char]$_})
Write-Output $rand
-
存储在环境变量或密钥管理服务(KMS/HashiCorp Vault)中,不要提交到代码仓库
-
定期轮换密钥,同时维护旧密钥的短时窗口以平滑过渡
-
固件签名私钥使用硬件安全模块(HSM)或受控构建机管理