一个相当白帽子的黑客已经展示了将他自己选择的文本插入到 Java 小程序和基于 Web 的服务器之间的通信中的能力。不是简单的 MITM 攻击,而是使用“JavaSnoop”之类的工具来利用 Java 小程序类结构中的通信。
我相信最终无法防御这种攻击(在这里我欢迎被反驳)——他原则上可以替代他自己选择的整个客户;当然,服务器必须针对各种恶意输入进行防护。
这方面的典型示例是通过更改客户端或客户端与服务器之间的通信在在线游戏中作弊。
有什么比默默无闻的安全性更能让这个过程尽可能困难的吗?
一个相当白帽子的黑客已经展示了将他自己选择的文本插入到 Java 小程序和基于 Web 的服务器之间的通信中的能力。不是简单的 MITM 攻击,而是使用“JavaSnoop”之类的工具来利用 Java 小程序类结构中的通信。
我相信最终无法防御这种攻击(在这里我欢迎被反驳)——他原则上可以替代他自己选择的整个客户;当然,服务器必须针对各种恶意输入进行防护。
这方面的典型示例是通过更改客户端或客户端与服务器之间的通信在在线游戏中作弊。
有什么比默默无闻的安全性更能让这个过程尽可能困难的吗?
JavaSnoop 是一种利用漏洞的工具,例如CWE-602: Client-Side Enforcement Of Server-Side Security。即使是胖客户端也不可信,如果分布式系统将敏感功能暴露给胖客户端, 那就是漏洞。防御“JavaSnoop”甚至“Firebug”或“burp”是没有意义的,这些只是工具。需要考虑整个系统,并且必须审查服务器是否存在可通过任何方式访问的远程可利用漏洞。
枚举这些类型的漏洞的过程与任何其他漏洞没有什么不同。评估团队或渗透测试人员将查看暴露的功能并测试此功能是否存在常见漏洞,例如 SQL 注入或信任边界问题。
让我们更具体地了解您的示例,并说这是一个在线扑克游戏。服务器包含代表桌子中心的数据(包括底池、面朝下的牌组和纸牌的“社区”),但客户端软件控制着他们桌子的“角落”(玩家的藏匿处、他们的手牌、和他们的决定)。
假设客户端软件与游戏作者发布的软件相同,没有进行任何修改,因此客户端软件已负责准确跟踪他们的资金和手牌;服务器向客户端“发”牌,就像实际的赌场发牌员一样,“忘记”(或永远不知道)发了什么牌。
这不是一个安全的假设;可以操纵客户端程序,甚至只是发送到它的消息的人,可以通过修改有关服务器发送的卡片的消息来选择他们的手,并且可以类似地增加他们的实际奖金(甚至通过转身忽略损失) “你输了”变成了“你赢了 1000 美元”)。
解决方案是不要让客户端软件有任何接近这种控制级别的东西。遵循的模型是“哑终端”;将客户端软件视为将键盘和显示器连接到服务器计算机的非常长的电缆。客户端只知道服务器告诉它的内容,并且除了将用户的输入中继到服务器之外什么都不做,反之亦然。它没有自己的“业务逻辑”,它只是向用户展示游戏。
给定这样的模型,操纵通信对攻击者没有好处;来自服务器的通信和屏幕上的数字和卡片可以更改为攻击者的内心内容,但任何基于客户端不正确数据的操作都会被服务器“砰”的一声带回现实。客户不能说“我筹到了 50 大”;服务器将简单地回复“您的堆栈中只有 20 美元;再试一次”。客户不能说“我是鲍勃,我打电话”;服务器看到请求来自 Bill 的安全会话,会说“不,你是 Bill,坐下来闭嘴,直到 Bob 真正轮到他”。甚至重放攻击,一个客户端可以监听另一个客户端和服务器之间的安全对话,并重复通信以执行其中包含的任何命令,很容易被检测到和忽略。鉴于这些轻率的通信足够多,服务器最终可能会说“你在浪费我的时间;再见”并将客户端踢出游戏。
如前所述,缺点是延迟。理想中的策略适用于扑克游戏,每个人都轮流行动,因此无论如何都有很多等待,而服务器同时跟踪所有事情是微不足道的。它不适用于 FPS 或 RTS,所有玩家之间的交互必须是实时的或非常接近的,并且有大量的弹丸和物体移动、飞行、碰撞等计算。延迟时会导致问题超过几毫秒(无论数据速率如何);如果每个人都有 150 毫秒 ping 到游戏服务器,那么每个人都会看到其他人在哪里300 毫秒前(至少)并且如果有人在对手的头部处于十字准线中时扣动扳机,服务器会认为他们正在射击该人实际上在半秒前的位置并说“你错过了”。这需要玩家“滞后领先”,根据他们的综合延迟在目标前方射击一段距离,即使游戏的物理特性要求子弹移动是瞬时的。
为了弥补这一点,服务器必须放弃一些控制,并让客户端在玩家扣动扳机时说“我朝 Bob 的头部开了枪”,而 Bob 的头部正出现在屏幕上的十字准线中。但是,拥有可以战略性地“忽略”有关其他玩家位置的传入数据的游戏模组的玩家可以操纵这种数量的客户信任来执行“冻结帧”黑客;关闭传入的数据报,其他所有人都会原地不动,让攻击者轻松爆头。如果服务器相信客户的说法,因为没有其他人声称他们先开枪打死了那个人,那么即使他自己的客户显示他安全地脱离了火线,另一个人也已经死了。
对于这种事情,真的没有最好的答案;任何你控制做出改变游戏规则的“裁判”类型决定的地方,玩家都会指责其他人作弊,因为他们在近距离范围内向那个人清空了一个剪辑并且服务器说他们打空了,或者因为服务器说“比尔死了,鲍勃开枪打死了他”,在比尔认为他已经清除了鲍勃在障碍物后面的火线之后整整两秒钟。
据我所知,您正在强调在线游戏中一个长期存在的问题:信任客户。粗暴地总结多年的研究和经验:
当我说验证一切时,我的意思是一切。你举了一个射击游戏的例子,恶意用户可能会添加更多的子弹。服务器应该知道玩家有什么枪,它可以发射多快,在重新加载之前可以发射多少发子弹等等。鉴于此,以及用户按下开火按钮的时间,服务器可以知道那里有多少子弹应该。客户不控制子弹;它告诉服务器玩家正在射击,并且服务器产生子弹。假设服务器会这样做,客户端可以自由地模拟子弹,但是当它归结为它时,在任何分歧中都是服务器的版本。
一般的教训是客户永远不会决定任何事情的结果。它只是将用户的操作通知服务器,并显示服务器发回的结果。如果有任何决定会影响事情的结果,则服务器会做出该决定。这归结为基础 - 客户没有说“枪在开火”,它说“扳机被拉动”。您的客户端在重新加载时可能会有延迟,但恶意客户端可以忽略它并立即发送“触发”消息。您的服务器不应允许这样做。
同时,消息到达服务器...
当消息返回给客户端时...
这就是为什么您会看到延迟会导致奇怪的效果 - 例如,当您似乎在四处移动时,这是因为客户端收到了“向前走”命令,并显示了您向前移动一点的假设结果。服务器没有得到命令,所以它告诉客户端“不,你在这里”,并且因为服务器总是正确的,客户端必须服从,你突然发现自己在别处。不过,一般来说,服务器和客户端会达成一致,因此客户端的这种假设结果模拟可以让游戏顺利运行;例如,您可能正在以每秒 80 帧的速度运行游戏,而来自服务器的更新每秒只有 30 次。对于更新之间的那几帧,客户端的假设足够接近,玩家不会注意到差异。
该过程中的验证阶段是您捕获作弊者的地方。任何重复提交不应被允许的命令的人都可能试图作弊。有可能他们的连接非常糟糕,服务器只是在接收乱序或丢失位的东西,但有一些方法可以确定这一点(TCP 开箱即用,但游戏经常使用 UDP,因为 TCP 可以较慢,因此您可能必须自己实现这些东西)。
正确的解决方案将取决于应用程序:如何防止误用取决于在您的上下文中误用的含义。
但在一般意义上,客户端不当行为的解决方案是服务器端验证。这可能意味着很多事情,所以让我举几个例子:
对于 Web 应用程序,使用 Javascript 进行验证不被视为真正的安全措施;提交响应后,必须再次完成客户端完成的任何安全检查。
再次使用 Web 应用程序,您不会假设您的输入将由性能良好的浏览器生成。例如,浏览器不会发送包含 的 URL /../,但无论如何您都会对其进行过滤。
对于诸如 Team Fortress 等在线 FPS 游戏,服务器会仔细观察诸如玩家移动速度、跳多高、瞄准的谨慎程度等指标,以挑选出不会由适当的行为产生的行为。 - 行为客户。
确保线路另一端的客户端是您的软件、正确配置、未修改且不受其他形式的篡改,这是绝对、完全或完全不可能的。
您绝对必须验证服务器端所有重要的内容。你可以安全,这是没有问题的。您可以防止多种作弊行为。但是您不会通过确保客户端未修改来做到这一点。相反,您必须保护服务器。