4. 设备身份与访问控制工程

影子读写是设备影子最核心的运行时行为:设备通过 update 上报状态、通过 get 拉取期望值,应用侧通过 update 下发期望、通过 get 读取上报。一旦身份与权限设计不到位,攻击者只要拿到一个设备凭证,就可能越权读取他人影子、伪造期望值下发,甚至批量接管设备。本章围绕“设备唯一标识—证书体系—令牌机制—最小权限策略”这条身份链,讲清在影子读写场景中如何真正落地。

一、设备唯一标识:身份链的根

设备唯一标识不是简单的“设备编号”,而是贯穿证书、令牌、策略、审计日志的主键。工程上通常采用三段式:产品标识(ProductKey/ProductID)+ 设备标识(DeviceName/SN)+ 实例标识(InstanceId)。产品标识区分设备型号与策略模板,设备标识区分同型号下的单台设备,实例标识用于区分同一物理设备在不同环境(生产/测试/灰度)中的身份,避免测试凭证流入生产。

标识生成必须满足三个约束:全局唯一、不可猜测、不可篡改。常见做法是设备出厂时由产线系统生成 128 位随机 DeviceSecret 或直接签发证书,SN 只作为人类可读的辅助标识,绝不作为鉴权依据。影子读写时,服务端从 TLS 客户端证书或令牌中提取标识,而不是信任报文里携带的 deviceId 字段——后者可被伪造。

二、证书体系:双向 TLS 是影子通道的底座

设备与影子服务之间推荐使用 mTLS(双向 TLS)。设备侧持有客户端证书与私钥,服务端持有服务端证书,双方在握手阶段互相验证。证书体系的关键参数包括:

  • 密钥算法:优先 ECDSA P-256,资源受限设备可用 RSA-2048;
  • 证书有效期:设备证书建议 1~2 年,配合轮换机制;
  • 吊销机制:CRL 或 OCSP,设备被回收或私钥泄露时立即吊销;
  • 证书链:设备证书 → 产品中间 CA → 根 CA,便于按产品批量吊销。

影子读写中,服务端在 TLS 层完成身份认证后,将证书中的 CN 或 SAN 映射为设备唯一标识,再进入授权判断。这样即使报文被篡改,攻击者也无法冒充其他设备,因为私钥不在其手中。

三、令牌机制:短时凭证与最小暴露面

对于资源受限或无法长期持有私钥的设备,可采用“长期身份 + 短期令牌”的两级机制:设备用证书或预置密钥向认证服务换取短期访问令牌(如 JWT),令牌中携带设备标识、权限范围、有效期(建议 5~60 分钟)。影子读写请求携带令牌,服务端验签并校验有效期与权限范围。

令牌设计要点:

  • 签名算法:RS256 或 ES256,禁止 none;
  • 声明字段:sub(设备标识)、scope(如 shadow:read:self)、exp、jti(防重放);
  • 绑定:令牌与设备标识、TLS 会话或客户端指纹绑定,防止令牌被复制到其他设备使用;
  • 刷新:短期令牌过期后用长期凭证刷新,刷新频率与吊销窗口需权衡。

四、最小权限策略:影子读写的授权模型

最小权限的核心是“默认拒绝、按需授予、按资源粒度收敛”。影子读写可抽象为四类操作:shadow:get:self、shadow:update:self、shadow:get:any、shadow:update:any。设备通常只应拥有前两者,且只能操作自己的影子;应用侧按业务角色授予后两者,并进一步限制到具体产品、设备组或字段路径。

策略表达可采用类似下面的结构(示例,非实测配置):

{
  "version": "2024-01",
  "statement": [
    {
      "effect": "allow",
      "principal": "device:productA/device001",
      "action": ["shadow:get:self", "shadow:update:self"],
      "resource": "shadow:productA/device001",
      "condition": {
        "ipRange": "10.0.0.0/8",
        "tlsClientCert": "required"
      }
    },
    {
      "effect": "deny",
      "principal": "device:*",
      "action": ["shadow:update:any"],
      "resource": "*"
    }
  ]
}

注意 deny 显式拒绝的优先级高于 allow,用于兜底防止越权。字段级最小权限还可进一步限制:设备只能上报 reported.temperature,不能写入 desired.firmwareUrl,避免设备伪造期望值触发恶意升级。

五、落地实践:一次影子读写的完整身份链

下面用一张图展示设备发起一次影子 update 时,身份与权限在各环节的传递与校验关系。图中左侧为设备侧,右侧为影子服务侧,中间为认证与授权组件。

设备侧 设备唯一标识 ProductKey/DeviceName 客户端证书 + 私钥 ECDSA P-256 短期访问令牌 JWT / scope / exp 影子 update 请求 reported.temperature 认证与授权组件 mTLS 握手校验 证书链 / CRL / OCSP 标识映射 CN/SAN → deviceId 令牌验签与声明校验 scope / exp / jti 最小权限策略判定 allow / deny / condition 影子服务侧 影子存储 reported / desired 版本号与时间戳 version / ts 审计日志 deviceId / action / result 响应与错误码 200 / 403 / 409 授权结果与影子版本返回 身份链:唯一标识 → 证书 → 令牌 → 策略 → 影子读写

图中可以看到,设备标识、证书、令牌、策略四者并非孤立,而是逐层收敛:证书解决“你是谁”,令牌解决“你能在什么时间窗口内做什么”,策略解决“你能对哪些资源做什么”。任何一层缺失,影子读写都会出现越权风险。

六、常见落地问题与判断依据

实践中,影子读写身份链最容易出问题的地方有三处。第一,标识与证书不一致:设备用 A 证书申请了 B 设备的令牌,服务端若只校验令牌签名而不校验 sub 与证书标识是否一致,就会越权。判断依据是审计日志中 deviceId 与证书 CN 是否匹配。第二,令牌有效期过长:超过 24 小时的令牌一旦泄露,吊销窗口过大。建议结合影子读写的实际频率,将有效期控制在分钟级,并用 jti 防重放。第三,策略粒度过粗:只授予 shadow:* 而不区分 self/any,设备可读写他人影子。判断依据是策略模拟测试:用设备凭证尝试 get 其他设备影子,应返回 403。

建议在设备接入测试阶段,用“越权用例集”验证身份链:伪造 deviceId、过期令牌、跨产品证书、字段越权写入,四类用例全部返回 403 或 401,才认为最小权限策略生效。

最后需要强调,影子读写的身份与访问控制不是一次性配置,而是随设备生命周期持续演进的工程能力。设备出厂、激活、轮换、回收、吊销,每个阶段都要有对应的标识与凭证操作,并与影子服务的授权策略保持同步。只有把唯一标识、证书、令牌、最小权限四者串成闭环,影子数据的安全才有可验证的底座。