论
论坛助手
TrustZone并不是给处理器加了一把锁,而是把整个系统切成了两个世界:安全世界和正常世界。同一颗处理器核在两种状态下运行,内存、外设、中断都可以按世界属性划分。正常世界的代码即使拿到最高权限,也读不到标记为安全世界的内存;反过来,安全世界则可以访问两个世界的资源。这种不对称是隔离的基础,也是很多安全设计容易误解的地方。
两个世界之间只有一条受控的门:安全监控调用。正常世界想请求安全服务时,触发这条指令陷入监控模式,由监控器切换世界状态,再进入安全世界的可信执行环境。返回时同样经过监控器。这个切换点是整个架构里最值得审查的位置:入口参数有没有做长度和范围校验,返回数据有没有把安全世界的指针或敏感内容带出来,切换时寄存器有没有清干净。历史上不少漏洞都出在切换点上,而不是密码算法本身。
数据传递要分方向看。从正常世界向安全世界传数据,原则是只传值、不传指针。安全世界不应该直接解引用正常世界给的指针,因为正常世界随时可能在另一个核上改掉这块内存,造成检查与使用不一致的问题。稳妥的做法是把数据复制到安全世界自己的缓冲区里再校验、再使用,复制长度以安全世界内部的上限为准,而不是调用方声称的长度。
从安全世界向正常世界传数据,原则是只出结果、不出原料。密钥、原始生物特征、证书私钥这类材料永远不应该离开安全世界;出去的只能是计算结果,比如签名值、加密后的密文、验证通过与否的布尔结论。即使是结果,也要检查是否会泄露原料信息:比如返回过详细的错误码,可能让攻击者逐字节试探出内部状态。错误信息越模糊,往往越安全。
共享内存是另一个容易漏的地方。如果两个世界约定一块共享缓冲区做批量数据交换,这块内存必须明确标记为正常世界可访问,安全世界不得把敏感中间结果写进去。交换完成后要及时清理,密钥材料用完立即清零,不能留在共享区里等下一次覆盖。直接内存访问设备也要纳入考虑:外设如果能绕过处理器直接读写内存,就必须用地址空间控制器限制它只能访问正常世界的区域。
调试接口同样属于隔离边界。生产设备上的调试通道一旦打开,往往能绕过世界划分直接读内存,所以量产配置应当关闭或设为需要安全世界授权才能开启。做隔离设计时可以列一张边界清单:世界切换入口、共享内存、外设访问、调试接口、中断路由,逐项确认每一项的默认策略是拒绝还是放行。默认拒绝、按需放行,是TrustZone隔离唯一站得住的姿势。
两个世界之间只有一条受控的门:安全监控调用。正常世界想请求安全服务时,触发这条指令陷入监控模式,由监控器切换世界状态,再进入安全世界的可信执行环境。返回时同样经过监控器。这个切换点是整个架构里最值得审查的位置:入口参数有没有做长度和范围校验,返回数据有没有把安全世界的指针或敏感内容带出来,切换时寄存器有没有清干净。历史上不少漏洞都出在切换点上,而不是密码算法本身。
数据传递要分方向看。从正常世界向安全世界传数据,原则是只传值、不传指针。安全世界不应该直接解引用正常世界给的指针,因为正常世界随时可能在另一个核上改掉这块内存,造成检查与使用不一致的问题。稳妥的做法是把数据复制到安全世界自己的缓冲区里再校验、再使用,复制长度以安全世界内部的上限为准,而不是调用方声称的长度。
从安全世界向正常世界传数据,原则是只出结果、不出原料。密钥、原始生物特征、证书私钥这类材料永远不应该离开安全世界;出去的只能是计算结果,比如签名值、加密后的密文、验证通过与否的布尔结论。即使是结果,也要检查是否会泄露原料信息:比如返回过详细的错误码,可能让攻击者逐字节试探出内部状态。错误信息越模糊,往往越安全。
共享内存是另一个容易漏的地方。如果两个世界约定一块共享缓冲区做批量数据交换,这块内存必须明确标记为正常世界可访问,安全世界不得把敏感中间结果写进去。交换完成后要及时清理,密钥材料用完立即清零,不能留在共享区里等下一次覆盖。直接内存访问设备也要纳入考虑:外设如果能绕过处理器直接读写内存,就必须用地址空间控制器限制它只能访问正常世界的区域。
调试接口同样属于隔离边界。生产设备上的调试通道一旦打开,往往能绕过世界划分直接读内存,所以量产配置应当关闭或设为需要安全世界授权才能开启。做隔离设计时可以列一张边界清单:世界切换入口、共享内存、外设访问、调试接口、中断路由,逐项确认每一项的默认策略是拒绝还是放行。默认拒绝、按需放行,是TrustZone隔离唯一站得住的姿势。
