我正在创建一个 Web 应用程序,其中包含一个简单的 javascript 游戏。一旦玩家完成游戏,高分将被发送到服务器并保存。
在特定时期后,得分最高的玩家将获得奖励。
有没有办法安全地发送高分并防止客户端发送“假”高分?
目前我们正在使用:
- 网址。
- 每场比赛前服务器随机令牌,并在比赛结束后与得分一起发送。
我正在创建一个 Web 应用程序,其中包含一个简单的 javascript 游戏。一旦玩家完成游戏,高分将被发送到服务器并保存。
在特定时期后,得分最高的玩家将获得奖励。
有没有办法安全地发送高分并防止客户端发送“假”高分?
目前我们正在使用:
服务器不能完全信任它从客户端接收到的任何数据,因此很难验证高分。
这里有几个选项:
混淆客户端代码和到服务器的流量。这是最简单的选择——它仍然可以作弊,但可能不值得任何人花时间。
将游戏的全部或部分重播发送到服务器进行验证。玩家的动作可以在服务器上运行以确定合法分数。这将适用于某些游戏,而不适用于其他游戏。
将游戏逻辑和验证移至服务器。客户端只需将每个动作中继到服务器,在服务器上对其进行验证并更新游戏状态。
这取决于你的游戏是如何运作的,人们作弊的可能性有多大,以及你对他们是否作弊的关心程度。
如果您的攻击者是网络层的中间人攻击者,那么 https 就足够了。但不幸的是,您的攻击者是客户端,所以这是毫无意义的。
防作弊游戏的第一条规则:永远不要相信客户!客户掌握在敌人手中。
Javascript 游戏在用户的网络浏览器中运行。正如您作为 Web 开发人员肯定知道的那样,现在每个 Web 浏览器都带有一个内置调试器,可以在应用程序运行时查看和更改所有变量。用户可以简单地使用调试器在提交之前更改他们的分数。您无法采取任何措施来防止这种情况发生。对于基于浏览器的游戏,您甚至无法在其上使用第 3 方反作弊工具(无论如何,这些工具的价值令人怀疑并且在道德上存在问题)。
唯一的对策是在服务器上实现所有值得操纵的游戏机制。客户端应该只将用户的命令转发到服务器并根据服务器的消息可视化游戏玩法。实际的游戏玩法应该发生在作弊者无法触及的服务器上。
是的,这意味着您必须从头开始重新设计游戏的软件架构。这也意味着您将不得不添加一些预测和插值代码以使网络延迟不那么明显。这也意味着您将需要更好的服务器硬件和更好的服务器互联网连接。但是,当您想要一款无作弊的在线游戏时,这是唯一的选择。
要获得安全的游戏高分,您需要的是“工作证明”,或者更恰当地说,称为游戏证明。
工作/游戏证明是在完成游戏之前难以计算的任何数据,但一旦完成游戏就很容易计算并且很容易被服务器验证。例如,对于数独游戏,工作量证明是解决难题的方法。
不幸的是,没有通用的方法可以在许多游戏中集成合适的游戏证明系统。在不调整或重新调整游戏玩法或将排行榜的范围限制在可以包含足够安全的游戏证明的元素的情况下,并不总是可以集成游戏证明。
游戏的证明从为游戏的 RNG 发出种子的服务器开始。然后,客户将必须找到与玩家发出的种子相匹配的游戏证明。在许多游戏中,可以通过将游戏的解决方案以高分提交的方式提交到服务器来形成游戏证明,而在其他游戏中,可能需要根据游戏和证明以不同级别的详细信息记录整个游戏会话。玩你正在使用。
游戏证明需要是抗重放的(一个玩家不应该能够使用另一个玩家完成的游戏来使他们自己的游戏证明更容易计算)。这将游戏证明限制在存在一些随机性的游戏中,玩家不能只是重复使用另一个玩家的游戏证明,而且它也不能有太多非确定性行为,以至于无法验证游戏。
游戏的证明也是有限的。例如,在数独游戏中,无法证明玩家解决谜题所花费的时间。游戏证明也不能总是区分玩家自己玩的游戏与为他们编写游戏脚本的玩家。
我只是想提一下这个问题有一种解决方案,但它可能对你不可用;该解决方案称为“同态加密”,它允许客户端执行已知计算而不知道他们正在计算的确切值,并且服务器因此检查计算值的结构以证明客户端不只是发回一个随机字符串,但实际上是根据提供的几个值构建的。
问题是,它要么很慢,要么不完整,“不完整”的意思是“没有包含完整的逻辑原语”。很多时候,它是一个部分系统,允许进行一些加密和解密操作 E()、D(),加上一些复杂的操作 ⊕ 使得
D(E(A) ⊕ E(B)) = A + B,
之类的。
那么让我们看看这如何解决一些游戏的问题。考虑一个“熄灯”类型的游戏,您在其中按下六边形网格的N个六边形,每个六边形都切换其状态和周围的状态。这是那些“压力机”的交换代数,因此在给定的解决方案中总共不会超过 N 次压力机,而且每个十六进制只取决于初始状态加上它自己的十六进制的压力加上它周围的 6 个六边形。
由于我们的同态加密方案只允许 +,而不是 XOR,我们给每个十六进制数一个 3 位计数器来表示它被翻转的次数。(客户端软件将自动将每次双击十六进制缩小为仅按一次。)因此,实际的翻转动作是位向量,看起来像,
001 001 000 000 000 001 001 001 000 001 001 000 000 ... 000 00000000 00000001
换句话说,它们在它翻转的每个 3 位字段中都有 1,在某些 16 位计数器中加上 1。
我们使用同态加密方案对所有这些进行加密,将它们中的每一个发送给客户端,然后客户端将根据我们发回的这些加密值计算出的加密值发回给我们。然后我们解密这个和解密的值与位串,
001 001 001 001 ... 001 11111111 00000000
并与那 8 个计数器位与 0 相邻的初始游戏状态进行比较。
如果他们向我们发送一个随机值,我们接受它的机会是 2 -(N+8),因此他们通过测试的唯一有用方法是使用我们给他们的值以某种允许的组合。由于整数溢出,它们可以访问一些我们不允许它们直接进行的移动,但我们总是可以使字段宽于 3 位,以使这些对于右侧的计数器来说更昂贵。但我们从来没有收到他们单独的按钮按下,更不用说重播历史了:我们接受了一个向量,上面写着“这就是我如何翻转网格”,这是每个人都在警告你的“超级不安全的事情”,但我们这样做的方式是,如果不访问密钥,他们就无法做我们担心的事情。
相反,您会在加密投票中看到这种情况,您需要确保投票机不会自发地给 Alice 投 1000 票。