如何防止用户篡改客户端JavaScript游戏的关卡进度?
完全客户端运行的游戏没法做到100%防篡改(毕竟所有代码都在用户设备上),但可以通过以下方法大幅提高篡改门槛,挡住绝大多数普通用户:
加密/签名存储进度
不要直接存明文关卡号,而是给关卡数据加上签名或加密后再存储。比如用HMAC签名:存储时把关卡号和一个隐藏的密钥生成哈希值,和关卡号一起存在localStorage(别用cookie,容量小还容易被看到);加载时重新计算哈希,和存储的对比,不一致就说明被篡改,直接重置到真实的最高进度。
示例代码:// 存储进度(密钥要混淆到代码里,别直接写明文) const secret = 'your-obfuscated-secret-key'; const currentLevel = 3; const sig = CryptoJS.HmacSHA256(currentLevel.toString(), secret).toString(); localStorage.setItem('gameProgress', JSON.stringify({ lvl: currentLevel, sig })); // 加载时验证 const saved = JSON.parse(localStorage.getItem('gameProgress')) || { lvl: 1 }; const computedSig = CryptoJS.HmacSHA256(saved.lvl.toString(), secret).toString(); if (computedSig !== saved.sig) { // 篡改,重置到已完成的最高关卡 const completedLevels = JSON.parse(localStorage.getItem('completedLevels')) || []; saved.lvl = completedLevels.filter(Boolean).length + 1; }存储结构化进度而非单一关卡号
不要只存当前关卡,而是记录每个关卡的完成状态(比如一个布尔数组completedLevels,索引对应关卡,值表示是否完成)。加载时计算用户实际能进入的最高关卡:找到最后一个已完成的关卡,下一关就是当前可玩的关卡。就算用户篡改了当前关卡号,也会被这个校验逻辑覆盖。
比如用户改了当前关卡到10,但completedLevels里只有前3关是true,就自动把当前关卡重置到4。用IndexedDB替代Cookie/LocalStorage
IndexedDB适合存储更复杂的游戏进度数据,而且不像localStorage那样在开发者工具里一眼就能看到明文。结合前面的加密和结构化存储,能进一步提高篡改难度。混淆压缩JS代码
把进度验证、加密的核心逻辑用混淆工具(比如Terser、UglifyJS)处理,让代码难以阅读和修改。普通用户就算打开开发者工具,也很难找到篡改的关键点。可选:轻量后端校验(如果能接受部分后端交互)
如果可以加个简单的后端接口,每次用户完成关卡时提交验证,后端记录用户的最高关卡。加载游戏时先从后端拉取真实的最高关卡,覆盖本地存储的数据。这种方法能彻底防止篡改,但需要后端支持,不符合纯客户端的初始设定,所以作为可选方案。
内容的提问来源于stack exchange,提问作者pdp8

