ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity AssetBundle 热更链路安全排查:从 CDN 到本地缓存的全链路校验

Unity AssetBundle 热更链路安全排查:从 CDN 到本地缓存的全链路校验 前段时间我们线上又炸了一次热更。新版本放量后大概两小时客服群里开始刷同一条玩家反馈游戏卡在“正在更新资源 0%”的进度条上后台错误日志里全是DownloadError: HashMismatch。我第一反应是 CDN 回源没刷干净结果查了一圈真正的问题居然出在本地缓存目录里的一个半截文件上。这事之后我把整条 AssetBundle 热更新链路从 CDN 版本清单到本地持久化缓存重新捋了一遍才发现之前很多“看起来没问题”的地方其实是安全盲区。这篇文章就是那次排查的完整复盘。我会按一条 AB 热更请求的实际路径从远端版本清单、CDN 分发配置、下载器实现一直讲到本地缓存的隔离与自愈把每一环的安全校验拆开讲。适合正在维护 Unity 热更框架的客户端开发也适合准备给项目做热更安全加固的团队参考。1. 热更链路全景风险到底藏在哪些节点1.1 一条完整的 AB 热更请求要经过哪些节点先把链路画清楚后面排查才有坐标系。一个最常规的 Unity AssetBundle 热更流程是这样的客户端启动后先读取本地已缓存的版本信息有的项目叫local_version.json有的直接存在本地清单文件里然后向远端请求最新的版本清单version.json。这份清单会记录所有 AB 包的包名、版本号、Hash、Size以及可选的下载地址。拿到清单之后客户端拿它和本地文件列表做差量生成一个“需要下载的 AB 列表”再逐个通过 HTTP 从 CDN 拉取资源包。资源包下载完成后写入本地缓存目录做完整性校验最后AssetBundle.LoadFromFile或UnityWebRequestAssetBundle加载。注意这里每一个节点都可能出问题版本清单本身可能被篡改或返回陈旧缓存CDN 边缘节点可能缓存了脏数据AB 包在传输过程中可能被截断、被替换本地缓存目录可能被写入半截文件、损坏文件多版本资源混用可能导致加载错乱磁盘空间不足导致写入静默失败。以前很多项目只关心“下载完后校验一下 Hash 对不对”但这只是链路末端的补救。从安全角度看热更系统是一个完整的信任链每一个环节都要有校验和预案。1.2 哪些环节最容易出安全事故我把常见事故按链路位置列了个风险矩阵排查时可以对着它逐一筛查环节风险表现典型后果版本清单请求CDN 缓存了旧清单客户端一直认为没有新版本热更静默失效版本清单内容清单被篡改Hash 被替换下载到恶意 AB加载后执行异常逻辑CDN 回源源站文件更新但未刷新缓存玩家长时间下载旧资源AB 下载过程断流、超时、中间人替换下载文件不完整或内容错误本地缓存写入进程被杀、磁盘满留下半截文件下次下载校验失败本地缓存读取多版本目录串用加载到错误版本的 AB这里有个很反直觉的结论最危险的不一定是下载过程而是版本清单这一环。因为清单决定了你要信任哪些文件的 Hash。如果清单本身被篡改后面所有针对 AB 的校验全部形同虚设。1.3 安全排查的主线信任链从哪来我开始排查的时候问了自己一个问题客户端凭什么相信这份清单是真的顺着这个问题往下挖就理出了一条主线——信任链。正常的信任链是客户端内置一个公钥构建机持有私钥版本清单由构建机签名。客户端拿到清单后用内置公钥验证签名验签通过才认为清单里的 Hash 可信然后用这些可信 Hash 去校验下载的 AB。整套系统的安全性最终收敛到两件事私钥安不安全、公钥有没有被替换。很多项目会在热更流程里顺便把代码也更新了什么 dll、lua、配置全都在资源里下发。一旦客户端代码可以热更就意味着公钥理论上也可以被热更逻辑替换。这种设计不是不行但等于把信任链的根暴露在了远程内容里需要额外做保护。后面我会专门讲这块。2. 第一道关口CDN 清单的安全校验怎么做2.1 版本清单的组成与常见误区一份典型的版本清单长这样{ appVersion: 1.4.2, resVersion: 1057, files: [ { ab: ui/main_ui.ab, hash: 3f2a9c84e1d7..., size: 1048576, url: https://cdn.example.com/res/ui_main_ui_3f2a9c84.ab }, { ab: scene/battle_01.ab, hash: 7c1d02ab94e6..., size: 20971520 } ] }字段不用多但hash、size、version是刚需。url可填可不填不填就由客户端按规则拼接。这里我见过不少项目犯同一个错误清单放在 CDN 上但没有做任何签名文件本身也没有防篡改机制。开发者觉得“CDN 配了 https 就够了呀”实际上 https 只保证传输过程中没人偷看和篡改完全不保证 CDN 上存的文件本身是安全的。还有一个常见误区只对 AB 做 Hash 校验不对清单做防护。你想攻击者完全可以把清单里ui/main_ui.ab的 hash 改成自己恶意包的 hash客户端按这个新 hash 下载、校验照样能通过。所以必须对“Hash 本身”做信任保护这是很多人容易漏掉的一层。2.2 签名校验与防篡改为什么只校验 Hash 不够清单的安全方案业内最常见的是 RSA 签名。具体流程是构建机在生成清单时用私钥对清单内容做签名签名和清单一起发布到 CDN客户端下载清单后先取出签名用内置公钥验签验签通过再解析清单内容。签名算法建议用 SHA256虽然 Unity 老版本里RSACryptoServiceProvider默认支持 SHA1但能上 SHA256 就别用 SHA1安全强度差不少。C# 侧核心逻辑示意public bool VerifyManifest(byte[] manifestBytes, byte[] signature) { using (var rsa new RSACryptoServiceProvider()) { rsa.FromXmlString(PublicKeyXml); var sha256 new SHA256CryptoServiceProvider(); return rsa.VerifyData(manifestBytes, sha256, signature); } }对应地构建机上用私钥签名using (var rsa new RSACryptoServiceProvider()) { rsa.FromXmlString(PrivateKeyXml); var sha256 new SHA256CryptoServiceProvider(); byte[] signature rsa.SignData(manifestBytes, sha256); File.WriteAllBytes(version.json.sign, signature); }这里有几个实操细节私钥只存在构建机或打包机上永远不要提交到代码仓库。公钥要内置在客户端里不要放到可被热更覆盖的路径。如果项目必须支持代码热更也要单独留一份只读的公钥作为回退基准别把所有根信任都托付给远程内容。清单文件名、签名文件名建议带版本号比如version_1057.json和version_1057.json.sign。避免 CDN 缓存导致新旧版本互相覆盖。验签用到的公钥可以再拆成多级做成“主密钥签名次密钥次密钥签清单”的链式结构方便后续轮换密钥而不强制玩家更新客户端。这个有点复杂小项目不一定用得上但大 DAU 产品值得考虑。2.3 CDN 侧配置回源、缓存 TTL 与鉴权清单签名解决的是“内容是否被篡改”CDN 配置解决的是“客户端拿到的内容是不是最新的、是不是正规的”。先说最常见的坑源站文件更新了但 CDN 边缘节点还在给客户端返回旧文件。尤其是版本清单这种小文件容易被各级缓存命中。我的做法是清单类文件请求时带Cache-Control: no-cache并且 URL 里带版本参数强制绕过缓存CDN 控制台上对*.json、*.json.sign这类文件设置 0 缓存或短缓存AB 文件本身走长缓存因为文件名里已经带了 hash内容一变 hash 就变文件名就变天然免疫缓存污染。再说鉴权。CDN 上如果裸奔任何人都能拉你的 AB 包
返回列表