如何替换 md5 以将 Unity 游戏数据传输到远程服务器

信息安全 php 。网 md5 赌博
2021-08-13 13:40:03

TL;博士

我正在开发一个游戏系统,它在客户端上使用UnityScript和 C#,在服务器上使用 PHP。数据的 MD5 散列和共享密钥用于检查数据在传输过程中是否被修改。MD5 够用吗?我可以使用哪些其他哈希算法适用于所有三种语言?

更详细的问题

我在一个广泛使用的社区网站上遇到了一些关于流行的游戏开发平台Unity 的代码,我现在正在努力改进该代码的 MySQL、PHP 和安全性。

该代码使用在客户端和服务器之间共享的“密钥”值。来自客户端的所有消息都包括数据的散列(例如名称和分数)以及密钥,服务器在接受数据之前会检查该密钥。这基本上是通过的数据未被篡改的身份验证。

但是,因为它是 MD5,我认为监听网络流量的人可以轻松计算出密钥,然后将他们想要的任何数据发布到服务器。

所以我的问题是:

  • 这种现状是否值得改善?或者这是 MD5 的预期当前用途(截至 2017 年 1 月)?
  • 是否有另一种散列算法可以进一步改进/验证这种通信活动?请注意,该算法需要在 PHP、UnityScript 和 C# 中工作。

代码

  • 在 UnityScript(客户端)中:

    var hash=Md5.Md5Sum(name + score + secretKey);
    
  • 在 C#(客户端)中:

    string hash = MD5Test.Md5Sum(name + score + secretKey);
    
  • 在 PHP(服务器端)中:

    $secretKey = "mySecretKey"; // Change this value to match the value 
                                //stored in the client javascript below 
    
    $realHash = md5($_GET['name'] . $_GET['score'] . $secretKey); 
    if($realHash == $hash) {
           //interact with database 
    }
    

进一步的标准

  • 一些 Unity 程序员使用 UnityScript(一种类似 Python 的语言,在 .NET API 上具有类似 JavaScript 的语法),但不能假定已安装任何库。

  • 游戏开发人员不是PHP / MySQL 程序员,那么复杂或第 3 方 PHP/C#/Js 代码可能不会有帮助。

  • BCrypt 显然与 C# 一起使用是不明智的

  • 在这种情况下, BCrypt 需要一个静态盐(可能是从密钥派生的?),但它旨在使用随机盐。

  • PBKDF2 似乎比 BCrypt 更受欢迎,但可能非常慢,特别是在没有太多内存的移动设备上。

  • 不能指望与安全服务器打交道(如果只是......)。

  • 我对C# 安全库知之甚少,无法从所列内容中真正挑选出最佳选项。

  • 虽然概述的代码只是与高分更新有关,但此代码已在过去 - 并将在未来 - 用于将各种公共和私有数据传输到各种数据库。

  • 处理 PHP、UnityScript、C# 之间的哈希算法互操作性是一个比我预想的更大的障碍。如果只是PHP,我会使用password_hash.

这个问题的一些想法和背景:

我更新了标题,因为编辑后的标题似乎表明我不确定是否要更改 MD5,而知道我应该更改 MD5 是首先在这里提出问题的核心原因之一。

最初的问题是,我想更新这里(以及其他地方)给出的关于如何处理客户端计算机上的游戏和远程服务器上的数据存储之间的交互的糟糕代码建议。请记住,这是针对 Unity 初学者程序员的代码建议,该站点 [现在] 由 Unity Technologies 自己运行。
如果你看,原来的问题(上面链接)是使用 PHPMysql_函数(以及相当糟糕的无效 PDO 形式)。我觉得这将受益于重写。我看到原始代码还使用 md5 例程对预期数据进行哈希处理。当谈到替换 MD5 时,我既没有意识到编译项目文件的漏洞,也没有意识到使这个代码块实际上更安全所需的工作的大小/规模(在与服务器或客户端的交互上)数据)。我最初的任务是找到一个合适的 MD5 替代品,它可以在所需的各种语言(UnityScript、C#、PHP)中工作,因为我知道它的不足之处。我没有意识到(从这里的评论来看)闯入 exe 并获取硬编码数据实际上是多么乏味。

这个问题是不是关于比赛我做,是不是我的项目,并引述我打算替换代码不是我写的我阅读了很多关于人们以某种​​方式对信使进行尝试的评论,但这个问题来自我自己希望改善教学 wiki 网站上现有缺陷的愿望。我非常尊重在回答这个问题时所分享的知识,但我知道,在过去 6 个月探索 Unity 文档和学习网站的过程中,在保护本地应用程序和多人游戏或其他远程交互方面存在很大差距。

我在评论中看到很多回复者说乔治给出的答案是一个糟糕的答案——但它回答了我当时提出的具体问题。谢谢。

最后一点:
这篇来自 Luke Briggs 评论的 Blob 帖子强调了操作本地 Unity 游戏应用程序数据的过程非常简单。我完全不理解本地文件有多么脆弱......

4个回答

这种方法从根本上是有缺陷的。客户端上的任何东西都可以并且将会被玩家篡改。同样的问题使 DRM 站不住脚——用户拥有机器及其上的所有数据,包括您的可执行文件、内存中的数据等。将算法保密是行不通的(参见Kerckhoffs 的原则),因为它只需要一小部分大量的逆向工程工作来弄清楚你的代码在做什么。

例如,假设您的游戏客户端中有一个例程,它将关卡分数发布到服务器,使用任何形式的密码术来确保它不会通过网络被篡改。有很多方法可以解决这个问题:

  • 使用诸如Cheat Engine之类的内存编辑工具扫描当前分数(暂停,搜索分数,取消暂停,等待分数更改,再次搜索,重复直到找到包含该值的内存地址)并进行编辑。当关卡完成时,您的代码将愉快地将分数值视为合法,并上传到服务器。
  • 修改磁盘上的游戏可执行文件,以便您的“关卡完成”代码忽略实际得分值并选择不同的值。
  • 修改磁盘上的游戏可执行文件,使简单的事情(例如杀死一个怪物)将您的分数提高 1000 倍。
  • 修改磁盘上的游戏可执行文件,使您永远不会死,或拥有无限的通电,或一击必杀,或任何其他帮助的东西,这样您就可以轻松获得很高的分数。
  • 在游戏加载后在内存中执行任何这些修改,以便原始可执行文件保持完整(对于存在烦人的完整性检查的情况很有用)
  • 只需从流程外部公开“我们完成了一个关卡,现在上传分数”代码,以便任何人的程序都可以调用它。在本机代码中,您可以注入一个小存根并向导出表添加一个条目,或者直接将代码复制到您自己的可执行文件中。在 .NET 中,只需修改类和方法的可见性标志并将其导入新程序是很简单的。现在您可以提交您喜欢的任何分数值,甚至无需运行游戏。
  • 对游戏进行逆向工程并获取“秘密”密钥并编写您自己的应用程序以将得分值发送到服务器。

这只是冰山一角。对于更复杂的游戏,有各种各样的变通方法来解决反作弊和其他问题,但无论使用何种防御技巧,总会有一种方法会弄乱客户端值。

安全方法的关键特征是玩家计算机上的任何内容都不应该被信任当您的服务器代码收到来自玩家的数据包时,假设您的游戏甚至可能没有运行——它可能是一段完全自制的代码,包含所有内容。

保护多人游戏(或任何需要验证游戏状态的游戏)并不容易。这个问题甚至不是安全问题,而是可用性和性能问题。确保多人游戏安全的最简单方法是将整个游戏状态保存在服务器端,在那里执行和维护所有游戏逻辑,让客户端只将玩家输入发送到服务器(“用户单击鼠标, 用户按住 W 键”) 并将音频和视频呈现给播放器。这样做的问题在于,由于网络延迟,它不能制作出非常有趣的游戏,而且很难在服务器端进行扩展。相反,您必须在保持客户端和服务器端之间找到平衡。例如,“

我们在这里有一些关于游戏安全的问题,所以我建议阅读这些问题:

我还在 GameDev SE 上推荐了这些问题:

还有一篇来自 BlackHat EU 2013 的关于多人游戏安全性的精彩论文,以及一篇关于非权威 P2P 网络的文章,它们都可能有用。

这里存在三个问题:

  • 如多项式的回答中所述,您尝试做的事情从根本上以多种方式被误导。

  • 如果您仍然坚持这样做,那么您甚至没有使用正确的原语!验证消息需要MAC,而不是哈希。可以从散列中构建 MAC——这就是HMAC所做的——但它并不像看起来那么简单。您粘贴的代码正在尝试从哈希中天真地构建 MAC,而这些天真的结构通常会被破坏。

  • 是的,MD5 是一个糟糕的哈希函数。但这确实是您最不担心的问题……MD5 并不是这种设计的弱点。您正在寻求有关如何用 2 英寸厚的钢板更换窗户上的栏杆的建议——但您的前门是敞开的。将其证明为“更好的安全性”是没有意义的。

编辑:澄清一下,我不能说你的方法的安全性,只能说哈希部分。这里有一些强烈的评论不鼓励你的方法。(即一个好的散列可以是安全方法的一部分,也可以是不安全方法的一部分,就像一把好的锁可以用在好的门/框架或脆弱的门/框架上)

作为对您的 TL;DR 的回应,我可以非常肯定地说 SHA-2 是推荐的替代散列函数。

如果长度是一个问题,使用截断的 SHA-2 比使用 MD5 更好。

SHA-2 有 4 种尺寸:224、256、384 或 512;256 和 512 是最常见的。

请记住,这是一个通用(快速)哈希,不适合密码存储,但它适合确保数据完整性,如您的 TL;DR 建议的那样。

BCrypt 是一种慢速密码哈希,如果您需要一种单向加密,则可以使用它。(即密码永远不会被存储,只有它的哈希,并且很难暴力破解)

我的一个朋友曾经做过一个Flash游戏(是的,很久以前)并使用相同的方案:md5(authenticated data + secret key),然后在服务器上验证它。

我用一些工具对其进行了逆向工程,并在大约 30 分钟内找到了他使用的秘密值。游戏结束。

但是,是的,这是第二好的可能选择。不管使用 sha2 代替 md5,也不管使用 hmac 代替散列算法,用户输入永远都不可信。如果用户在他们的机器上运行您的游戏(即在他们的控制之下)并且如果他们可以提交高分,他们将始终能够使用工具来提高分数。

您可以做的最好的事情是上传回放(例如录制的按键)并在服务器上重新运行它,然后验证结果时间。这需要大量的代码编写工作,占用更多的服务器资源,如果你在任何地方使用浮点数,你将需要确定性物理......但这是唯一比你正在做的更好的事情,人们仍然可以编写脚本游戏,如果他们想作弊。