有哪些好的方法可以防止 JavaScript 多人游戏中作弊?

34

想象一下一个带有滚动关卡的太空射击游戏。有哪些方法可以防止恶意玩家修改游戏以获得利益?一些难以在服务器端限制的事情包括自动瞄准、窥视可见区域之外、速度作弊和其他一些行为。

有什么方法可以预防这种情况?假设服务器使用任何一种语言,客户端通过WebSocket连接。

始终假设代码100%可破解。考虑预防完全重写客户端(用于作弊)进行作弊的方法。这些可以是编写安全游戏协议的方法、服务器端检测等。


1
无论你做什么,一个人总是可以执行他想要的,因为实际上他拥有执行JavaScript的计算机。 - pimvdb
1
我怀疑这将会很困难,也许在当前浏览器的Javascript环境下是不可能防止的,但这是一个很好的思考题。 - samplebias
9个回答

39

服务器是王者,客户端易受攻击。

使用WebSocket时,您需要做两件事:

向服务器发送游戏操作并从服务器接收游戏状态。

渲染游戏状态并将输入发送到服务器。

  • 自动瞄准 - 这个很难解决。您必须考虑现实性。如果一个用户在10毫秒内命中了10个爆头,那么您会踢他。编写一个聪明的作弊检测算法。
  • 窥视可见区域之外 - 通过仅向每个客户端发送可见区域来解决
  • 加速作弊 - 通过正确处理输入来解决。您接收一个事件,表示用户A向前移动,而您控制他走的速度。

通过缩小代码,您无法解决这些问题。客户端上的代码用于处理输入和显示输出。所有逻辑都必须在服务器上完成。

您只需要编写服务器端的验证。唯一的问题是,由于复杂性,游戏输入比表单输入要难得多。这与使表单安全的确切相同。

但是,在“输入有效”检测方面,您需要非常小心。您不想从游戏中踢出/禁止高技能玩家。在机器人检测的严格度和宽松度之间取得平衡非常困难。整个机器人检测领域总体上非常难。例如,Quake曾经有过一种自动瞄准检测器,在当时会将正当技能的玩家踢出。

至于防止机器人直接连接到您的WebSocket,为多人游戏设置一个单独的HTTP或HTTPS验证通道可增加安全性。使用多个Http/https/ws通道来验证客户端是否“官方”,充当某种握手形式。这将使直接连接到ws变得更加困难。

示例:

想象一个简单的多人游戏。一个二维基于房间的赛车游戏。最多n个用户在平坦的二维平台地图上竞赛,以从A到B。

假设您有一个 foolproof 系统,其中通过 HTTPS 渠道进行了复杂的身份验证,以使用户无法直接访问 WebSocket 通道并被迫通过浏览器进行访问。您可能有一个 Chrome 扩展程序来处理身份验证,并强制用户使用该扩展程序。这减小了问题领域。
您的服务器将发送客户端需要呈现屏幕的所有可视数据。您无法将此数据隐藏起来。无论您尝试什么,一个熟练的黑客都可以拿走您的代码,在调试器中缓慢编辑它,直到他剩下的只是对您的 WebSocket 进行包装的基本代码。他让您运行整个身份验证过程,但是您无法阻止他剥离掉任何 JavaScript。
您编写的内容无法阻止他这样做。你所能做到的就是限制那些有足够技能访问WebSocket 的黑客的数量。
因此,黑客现在在 Chrome 沙盒中拥有您的WebSocket。他可以看到输入。当然,您的赛道是动态而独特生成的。如果您有固定的赛道,则黑客可以预先设计最佳的赛车路线。您发送的数据以可视化方式呈现地图,比人与游戏互动更快,并且可以计算出赢得赛车游戏的最佳移动,并将其发送到您的服务器。
如果您试图禁止对您的地图数据反应过快并称他们为机器人,则黑客会进行调整并添加延迟。如果您试图禁止玩得太完美的玩家,那么黑客将进行调整并使用随机数进行不太完美的游戏。如果在地图中放置只有算法机器人才会掉入的陷阱,则可以通过试错或机器学习算法来避免它们。没有绝对安全的方法可供您采取。
您只有一个选项可以绝对避免黑客。那就是建立您自己的浏览器,这个浏览器不能被黑客攻击。将安全机制构建到浏览器中。不要允许用户实时编辑 JavaScript。

感谢您的输入!其中一些点是防作弊的常规方法,我会很感激更高级的方法的建议。但所有的意见都是宝贵的。 :) - Blixt
@Blixt的高级方法是游戏领域特定的 ;) 我可以详细介绍如何检测FPS作弊者,但这对于检测平台游戏作弊者并不能帮助,因为他们可以做到几乎完美的跳跃,让人难以置信。 - Raynos
@Blixt 写一个安全的浏览器,内置一个JavaScript游戏引擎;同时让它具有P2P套接字,并使浏览器作为Node.js服务器运行。 - Raynos

3
在服务器端,有两个选项:
1)完全的服务器端游戏
每个客户端将自己的“动作”发送到服务器。服务器执行它们并发送相关数据回来。例如,一艘船想向北移动,服务器计算它的新位置并将其发送回来。服务器还发送可见船只的列表(解决地图黑客等等)。
2)完全的客户端游戏
每个客户端仍然将其行为发送到服务器。但是为了减轻服务器负担,服务器不执行这些操作,而是将它们转发给所有其他客户端。然后客户端同时解决所有操作。结果,每个客户端应该得到一个相同的游戏。定期地,每个客户端将其绝对数据(船只位置等)发送到服务器,并且服务器检查所有客户端数据是否相同。否则,游戏会出现不同步的情况,必须有人在黑客。
第二种方法的缺点是有些黑客仍然没有被检测到:例如地图黑客。作弊者可以注入代码,以便他看到所有东西,但仍然只将他正常情况下应该能看到的数据发送到服务器。
--
在客户端,只有一个选项:
一个Javascript组件扫描游戏代码,以查看是否进行了修改(例如,修改代码以呈现不可见的对象,但向服务器发送不同的验证数据)。
显然,黑客可以轻松地禁用此组件。为了解决这个问题,您可以强制客户端定期从服务器重新加载该组件(服务器可以周期性地检查用户是否请求了脚本文件)。这引入了一个新问题:黑客只需通过AJAX定期请求组件,但阻止其运行即可。为避免这种情况:组件会重新下载自身,但是它的稍微修改版本。
例如:将组件位于yoursite/cheatdetect.js?control=5。 服务器将生成略微修改的cheatdetect.js,以便在下一次迭代中,必须下载cheatdetect.js?control=22(例如)。如果控制机制足够复杂,黑客将无法预测下一个请求哪个控制号,必须执行cheatdetect.js才能继续游戏。

1

你无法真正防止任何人修改你的JS或编写GreaseMonkey脚本。但是,你可以通过缩小你的脚本并尽可能使你的代码加密来使它们难以修改。甚至可以添加一些虚假的方法或变量,这些方法或变量什么也不做,只是用于误导攻击者。但是,如果给予足够的时间,这些方法都不是完全可靠的,因为一旦你的代码传输到客户端,它就不再属于你。


1
是的,实际上我正在寻找防止作弊的方法,即使代码被修改也能发挥作用。因为这当然是一个作弊者会做的事情。:)我添加了第三段,提出了另一个假设。 - Blixt
这些“延迟”攻击者的方法只是对于代码修改者/用户和维护者来说是一种麻烦。 - Raynos
2
通过混淆来保障安全永远不是一个好主意,在任何地方都不应该提倡这种做法,这简直是愚蠢至极。即使你知道这一点,你的回答也不应该被写出来 :) - red

1

他们不必触碰您的客户端代码--他们只需嗅探并实现您的Websocket协议,并编写一个假装是人类玩家的微小代理。

更新:问题有几个部分,我没有即时答案,但可以考虑以下问题来评估各种选项:

  1. 您愿意采取多大程度的防作弊措施?如果您只关心偶尔的作弊行为,那么多少障碍足以阻止偶尔的作弊者?中级JavaScript程序员?专业的专家?将其与作弊的好处相比较,是否有任何真正有价值的东西,如现金和奖品,或仅仅是声誉?
  2. 如何获得高度信心,以确保人类提供输入到您的游戏中?例如,使用足够好的计算机视觉库,我可以在另一台机器上模拟您的游戏,向计算机提供输入,假装是鼠标,但这具有相对较高的成本(不值得我的时间)。
  3. 如何在您的协议中创建信任链,以便可以将(2)的知识传递给服务器,并且您的服务器相对自信您的客户端代码正在发送消息?

当然,你设置的许多路障可以被规避,但对玩家和你来说代价是什么?请参见 "消耗战。"


是的,这正是我正在寻找解决方案的假设。我已经阅读了一些论文,讨论了这个问题并提出了合理的缓解方法,但我正在寻求更多的意见。 - Blixt

1
我能想到的唯一实现方式是修改你的Javascript,使其作为客户端运行,并设计一个中央服务器机制来验证从该客户端发送的数据。这可能是一个很大的变化,很可能会使你的项目更加复杂。然而,正如之前所说,如果应用程序完全在客户端上运行,客户端几乎可以随意使用你的脚本。唯一保证安全的方法是使用可信任的机器来处理验证。

这是唯一真正的解决方案。尽可能将所有逻辑放在服务器上。服务器从每个客户端消耗事件并响应更新状态。如果客户端只是呈现当前状态并为每个用户交互发送事件,则无需担心。 - Prestaul
@BobBaller 只要 JavaScript 游戏是单人游戏,就没有必要在其中加入验证。如果用户想作弊,那么他会破坏自己的游戏体验。对于不会影响到单个游戏实例之外的任何事情的单人 JavaScript 游戏,您应该允许用户做任何他想做的事情。 - Raynos
@Raynos 的说法没错,但原问题特别涉及到需要验证以维护所有参与方公平性的多人 JavaScript 游戏。 - Matt McCarragher
@BobBailer 如果游戏已经是多人游戏,那么逻辑应该已经在中央服务器上运行。在客户端上进行点对点的多人游戏将会是一个难以控制的噩梦。 - Raynos
@Raynos 我刚刚重新阅读了这个问题,我认为它在我的第一篇帖子之后已经被编辑过了。我不记得最初提到过服务器,所以我假设它是点对点的。 - Matt McCarragher
@BobBailer 如果没有第三方应用程序,P2P在客户端中如何工作? - Raynos

1
一些其他可以实现的方法:
  • 让目标元素难以被脚本与其他元素区分。如果可能的话,避免使用具有可预测类和ID名称的div。使用JavaScript注入样式而不是使用类。像黑客一样思考,让自己难受。
  • 使用脚本将触发的诱饵。例如,如果威胁向量是使用像素颜色的屏幕抓取算法,则在非目标元素中放置一些常见的像素颜色。对这些非目标的命中可能对作弊者看来不重要,但是可以检测到。您不希望作弊者知道您为什么知道。
  • 将动作之间的最小时间限制在略低于最佳人类水平。最好的玩家会达到这个高度,谁在作弊并不重要,并且立即能够通过侧面调用方法调用来检测任何比那更快的脚本。
  • 随机数生成器通常是均匀的。人性不是。可能随机数生成器的值在一定范围内并具有均匀分布。自然分布是高斯曲线。如果您对分布进行采样,并且在x轴和y轴上看起来像方波,那么它就是一个作弊者。对于作弊者来说,这将是相当困难的,因为它是随机数的导数,而不是随机分布本身的阈值。您还使用聚合数据而不是个别游戏来检测它,因此在不知道您的检测算法的情况下,反向工程算法将非常困难。
  • 尽可能利用熵。避免可预测的游戏玩法。想象一下在一组赛车跑道上进行的赛车比赛。每个游戏玩法的牵引力,马力和动量都可能略有不同。脚本必须非常出色才能打败它。在滚动游戏中,您可以更改对人类本能而言很自然但对计算机而言很困难的因素,例如风力,重力变化等。这也会使其更有趣。
  • 服务器生成的令牌可用于验证已使用UI元素而不是调用代码本身。在比赛结束时,可以通过一次调用来处理验证,将事件与UI元素的哈希代码进行比较。令牌应该是一个具有服务器私钥和某些UI元素值的哈希。
  • 使用数据欺骗作弊者,让他们认为您正在使用来检测作弊的数据。例如,使用对虚假后端的虚假调用来调用DetectCheat方法。这是老魔术师的把戏。在这里挥动你的手,而你用另一只手将一张牌放入牌组中。让他们在没有出口的迷宫中浪费数天时间,让头发变得茂密。

0

您可以在浏览器上编辑JavaScript并使其工作。有些人建议向服务器发出调用以进行检查。因此,在向服务器发出调用后,将在服务器上进行验证。一旦验证通过,它将返回客户端并执行操作。但我认为即使这也不是绝对可靠的。

例如,对于基本的登录操作:在Angular中,当向服务器发出调用时,后端会验证用户名和密码,如果验证通过,它将返回客户端并允许用户使用Angular登录。

当我说使用Angular登录时,它将在cookie中存储用户对象和其他内容。但是,用户仍然可以删除调用后端的JS代码,并返回TRUE(在需要的任何地方),并将用户对象(虚拟)插入到cookie和其他对象(无论需要什么)中并登录。这是一件非常困难的事情,但是它是可行的。在许多情况下,即使花费数小时来编辑/黑客攻击代码,这也是不可取的。

这在单页应用程序中是可能的,其中JS文件不会为每个页面重新加载。为了减轻被黑客攻击的可能性,我们可以使用缩小的代码。而且,我想如果像Django中的后端操作(如登录)这样做,它会更安全。

如果我错了,请纠正我。


0
尽可能混淆你的客户端代码。此外,使用一些魔法。

2
这更像是一个玩笑,而不是对问题的真正回答。 - zmilan

0

我会使用缩小和AJAX的组合。如果所有的函数和数据都没有加载到页面中,那么作弊就更加困难了。

另一方面,像Id Software这样的公司发现modding是一个非常有利可图的工具。也许允许系统被修改可能会使游戏对整个社区更加有趣。


1
是的,想法是让人们可以查看和修改源代码,但仍然防止作弊。当然,用户修改代码始终会有好处,但我正在寻找减少这些好处而不影响游戏玩法的方法。 - Blixt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接