it-swarm.cn

什么是阻止人们破解基于PHP的Flash游戏高分表的最佳方法

我说的是没有上限分数的动作游戏,无法通过重放动作等来验证服务器上的分数。

我真正需要的是Flash/PHP中可能的最强加密,以及一种防止人们通过我的Flash文件调用PHP页面的方法。我曾尝试过一些简单的方法,可以对单个分数进行多次调用,完成校验和/斐波那契序列等,并且还使用Amayeta SWF加密对SWF进行模糊处理,但最终都被黑客攻击。

感谢StackOverflow的回复,我现在从Adobe找到了更多信息 - http://www.Adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.htmlhttps://github.com/mikechambers/ as3corelib - 我认为我可以用于加密。不知道这会让我想到CheatEngine。

如果它们不同,我需要知道AS2和AS3的最佳解决方案。

主要问题似乎是TamperData和LiveHTTP标题,但我知道还有更高级的黑客工具 - 比如CheatEngine(感谢Mark Webster)

212
Iain

这是互联网游戏和竞赛的经典问题。您的Flash代码与用户一起决定游戏的分数。但用户不受信任,Flash代码在用户的计算机上运行。你是SOL。您无法阻止攻击者伪造高分:

  • Flash比你想象的更容易进行逆向工程,因为字节码已经很好地记录并描述了一种高级语言(Actionscript)---当你发布一个Flash游戏时,你发布的源代码,无论你是知道与否。

  • 攻击者控制Flash解释器的运行时内存,这样任何知道如何使用可编程调试器的人都可以随时改变任何变量(包括当前得分),或者改变程序本身。

对您的系统的最简单的攻击是通过代理运行游戏的HTTP流量,捕获高分保存,并以更高的分数重放它。

您可以尝试通过将每个高分保存绑定到游戏的单个实例来阻止此攻击,例如通过在游戏启动时向客户端发送加密的令牌,这可能如下所示:

hex-encoding( AES(secret-key-stored-only-on-server, timestamp, user-id, random-number))

(您也可以使用会话cookie达到相同的效果)。

游戏代码通过高分保存将此令牌回传给服务器。但攻击者仍然可以再次启动游戏,获取令牌,然后立即将该令牌粘贴到重放的高分保存中。

接下来,您不仅要提供令牌或会话cookie,还要提供高分加密会话密钥。这将是一个128位AES密钥,本身使用硬编码加密到Flash游戏中的密钥进行加密:

hex-encoding( AES(key-hardcoded-in-flash-game, random-128-bit-key))

现在,在游戏发布高分之前,它会解密高分加密会话密钥,因为您将高分数加密会话密钥解密密钥硬编码到Flash二进制文件中。您使用此解密密钥加上高分,以及高分的SHA1哈希值:

hex-encoding( AES(random-128-bit-key-from-above, high-score, SHA1(high-score)))

服务器上的PHP代码检查令牌以确保请求来自有效的游戏实例,然后解密加密的高分,检查以确保高分符合高分的SHA1 (如果跳过这一步,解密将只产生随机的,可能非常高的高分)。

所以现在攻击者反编译你的Flash代码并快速找到AES代码,它像拇指一样伸出来,虽然它不会在15分钟内通过内存搜索和跟踪器跟踪(“我知道我对这个游戏的得分是666,所以让我们在内存中找到666,然后抓住任何触及该值的操作---哦,看看,高分加密代码!“)。使用会话密钥,攻击者甚至不必运行Flash代码;她抓住游戏启动令牌和会话密钥,可以发回任意高分。

你现在正处于大多数开发人员放弃的地步---通过以下方式给予或采取几个月的攻击行为:

  • 使用XOR操作对AES密钥进行加扰

  • 用计算密钥的函数替换关键字节数组

  • 在整个二进制文件中散布假密钥加密和高分帖子。

这大部分都是浪费时间。毋庸置疑,SSL也不会对你有所帮助;当两个SSL端点中的一个是邪恶的时,SSL无法保护您。

以下是一些可以减少高分欺诈的事情:

  • 需要登录才能玩游戏,登录会产生会话cookie,并且不允许在同一会话中启动多个未完成的游戏,或同一用户的多个并发会话。

  • 拒绝持续时间少于最短的真实游戏的游戏会话的高分(对于更复杂的方法,尝试“隔离”游戏会话的高分,其持续低于平均游戏持续时间少于2个标准偏差)。确保您在服务器端跟踪游戏持续时间。

  • 拒绝或隔离仅播放过一次或两次游戏的登录的高分,以便攻击者必须为他们创建的每次登录制作一个合理的游戏玩法的“纸质踪迹”。

  • 在游戏过程中“心跳”得分,以便您的服务器在一场比赛的整个生命周期中看到得分增长。拒绝不遵循合理分数曲线的高分(例如,从0跳到999999)。

  • 游戏过程中的“快照”游戏状态(例如,弹药数量,等级中的位置等),您可以稍后根据记录的中期分数进行协调。您甚至无需开始检测此数据中的异常;你必须收集它,然后如果事情看起来很腥,你可以回去分析它。

  • 禁用任何未通过安全检查的用户的帐户(例如,通过提交未通过验证的加密高分)。

请记住,你只是在这里阻止高分欺诈。有 没什么 你可以做以防止如果。如果游戏中的线路有钱,有人会打败你想出的任何系统。目标不是 停止 这次攻击;这是为了使攻击更加昂贵,而不仅仅是擅长游戏并击败它。

414
tqbf

你可能会问错误的问题。你似乎专注于人们用来在高分榜上进行游戏的方法,但阻止特定方法只是到目前为止。我对TamperData没有经验,所以我不能说。

您应该问的问题是:“我如何验证提交的分数是否有效且真实?”具体方法是依赖游戏。对于非常简单的益智游戏,您可以发送得分以及特定的起始状态和导致结束状态的移动顺序,然后使用相同的移动在服务器端重新运行游戏。确认所述分数与计算得分相同,并且只有在匹配时才接受分数。

25
Stephen Deken

一种简单的方法是提供高分值的加密哈希以及自己的分数。例如,通过HTTP GET发布结果时:http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

在计算此校验和时,应使用共享密钥;这个秘密永远不应该通过网络传输,但应该在PHP后端和flash前端内进行硬编码。上面的校验和是通过将字符串“ secret ”添加到得分“ 500 ”并通过md5sum运行来创建的。

虽然该系统将阻止用户发布任意分数,但它不会阻止“重放攻击”,其中用户重新发布先前计算的分数和散列组合。在上面的示例中,得分500将始终生成相同的哈希字符串。通过在要散列的字符串中包含更多信息(例如用户名,时间戳或IP地址),可以减轻某些风险。虽然这不会阻止重放数据,但它将确保一组数据一次只对单个用户有效。

为防止发生 任何 重放攻击,必须创建某种类型的质询 - 响应系统,如下所示:

  1. Flash游戏(“客户端”)执行HTTP GEThttp://example.com/highscores.php没有参数。此页面返回两个值:随机生成的salt值,以及该salt值与共享密钥组合的加密哈希值。此salt值应存储在待处理查询的本地数据库中,并且应该具有与之关联的时间戳,以便它可以在一分钟之后“到期”。
  2. Flash游戏将salt值与共享密钥组合在一起并计算哈希以验证它是否与服务器提供的哈希相匹配。此步骤对于防止用户篡改salt值是必要的,因为它验证salt值是否实际由服务器生成。
  3. Flash游戏将盐值与共享密钥,高分值以及任何其他相关信息(昵称,IP,时间戳)相结合,并计算哈希值。然后它通过HTTP GET或POST将此信息以及盐值,高分和其他信息发送回PHP后端。
  4. 服务器以与客户端相同的方式组合接收的信息,并计算散列以验证其是否与客户端提供的散列相匹配。然后,它还验证salt值是否仍然有效,如挂起查询列表中所列。如果这两个条件都为真,则将高分写入高分表并将签名的“成功”消息返回给客户端。它还会从挂起的查询列表中删除salt值。

请记住,如果用户可以访问共享密钥,则上述任何技术的安全性都会受到损害

作为替代方案,可以通过强制客户端通过HTTPS与服务器通信以及确保客户端预先配置为仅信任由您自己可以访问的特定证书颁发机构签名的证书来避免这种来回中的一些来回。 。

18
stormlash

我喜欢tpqf所说的,但是在发现作弊行为时,不是禁用帐户,而是实施蜜罐,因此每当他们登录时,他们就会看到他们的黑客攻击分数,并且从不怀疑他们被标记为巨魔。谷歌的“phpBB MOD巨魔”,你会看到一个巧妙的方法。

11
DGM

我做了一些解决方法......我得到了一个得分,其中得分增加(你总是获得+1分)。首先,我开始计算随机数(假设是14),当我显示分数时,只显示得分减去14.这是如此,如果破解者正在寻找20的例子,他们将找不到它(它将在记忆中34岁)。其次,既然我知道下一点应该是什么......我使用Adobe加密库来创建下一个点应该是的哈希值。当我必须增加分数时,我检查增量分数的散列是否等于散列应该是。如果饼干改变了记忆中的点数,则哈希值不相等。我执行一些服务器端验证,当我从游戏和PHP获得不同点时,我知道涉及作弊。这是我的代码片段(我正在使用Adobe Crypto libraty MD5类和随机加密盐.callPhp()是我的服务器端验证)

private function addPoint(event:Event = null):void{
            trace("expectedHash: " + expectedHash + "  || new hash: " + MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt) );
            if(expectedHash == MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt)){
                SCORES +=POINT;
                callPhp();
                expectedHash = MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt);
            } else {
                //trace("cheat engine usage");
            }
        }

使用这种技术+ SWF obfustication,我能够阻止破解者。此外,当我将分数发送到服务器端时,我使用自己的小型加密/解密功能。像这样的东西(不包括服务器端代码,但你可以看到算法并用PHP编写):

package  {

    import bassta.utils.Hash;

    public class ScoresEncoder {

        private static var ranChars:Array;
        private static var charsTable:Hash;

        public function ScoresEncoder() {

        }

        public static function init():void{

            ranChars = String("qwertyuiopasdfghjklzxcvbnm").split("")

            charsTable = new Hash({
                "0": "x",
                "1": "f",
                "2": "q",
                "3": "z",
                "4": "a",
                "5": "o",
                "6": "n",
                "7": "p",
                "8": "w",
                "9": "y"

            });

        }

        public static function encodeScore(_s:Number):String{

            var _fin:String = "";

            var scores:String = addLeadingZeros(_s);
            for(var i:uint = 0; i< scores.length; i++){
                //trace( scores.charAt(i) + " - > " + charsTable[ scores.charAt(i) ] );
                _fin += charsTable[ scores.charAt(i) ];
            }

            return _fin;

        }

        public static function decodeScore(_s:String):String{

            var _fin:String = "";

            var decoded:String = _s;

            for(var i:uint = 0; i< decoded.length; i++){
                //trace( decoded.charAt(i) + " - > "  + charsTable.getKey( decoded.charAt(i) ) );
                _fin += charsTable.getKey( decoded.charAt(i) );
            }

            return _fin;

        }

        public static function encodeScoreRand(_s:Number):String{
            var _fin:String = "";

            _fin += generateRandomChars(10) + encodeScore(_s) + generateRandomChars(3)

            return _fin;
        }

        public static function decodeScoreRand(_s:String):Number{

            var decodedString:String = _s;
            var decoded:Number;

            decodedString = decodedString.substring(10,13);         
            decodedString = decodeScore(decodedString);

            decoded = Number(decodedString);

            return decoded;
        }

        public static function generateRandomChars(_length:Number):String{

            var newRandChars:String = "";

            for(var i:uint = 0; i< _length; i++){
                newRandChars+= ranChars[ Math.ceil( Math.random()*ranChars.length-1 )];
            }

            return newRandChars;
        }

        private static function addLeadingZeros(_s:Number):String{

            var _fin:String;

            if(_s < 10 ){
                 _fin = "00" + _s.toString();
            }

            if(_s >= 10 && _s < 99 ) {
                 _fin = "0" + _s.toString();
            }

            if(_s >= 100 ) {
                _fin = _s.toString();
            }           

            return _fin;
        }


    }//end
}

然后我将变量与其他假vars一起发送,它只是在路上迷失了...这对于小型Flash游戏来说是很多工作,但是涉及到奖品时,有些人会变得贪婪。如果您需要任何帮助,请给我写一个PM。

干杯,Ico

在接受的答案中,tqbf提到您可以只对记分搜索变量(“我的分数是666,所以我在内存中查找数字666”)。

有一种解决方法。我在这里上课: http://divillysausages.com/blog/safenumber_and_safeint

基本上,您有一个对象来存储您的分数。在setter中,它将您传递的值乘以随机数(+和 - ),并在getter中将保存的值除以随机乘数以获得原始值。它很简单,但有助于停止内存搜索。

另外,请查看PushButton引擎背后的一些人的视频,他们谈论了一些可以打击黑客行为的不同方法: http://zaa.tv/2010/12/the-art-of-hacking- flash游戏/ 。他们是课堂背后的灵感来源。

4
divillysausages

您不能信任客户返回的任何数据。验证需要在服务器端执行。我不是游戏开发者,但我做的是商业软件。在这两种情况下都可以涉及金钱,人们会破坏客户端的混淆技术。

也许定期将数据发送回服务器并进行一些验证。不要专注于客户端代码,即使这是您的应用程序所在的位置。

3
zeller

使用已知(私有)可逆密钥进行加密将是最简单的方法。我不是所有的AS,所以我不确定有什么样的加密提供商。

但是你可以包括游戏长度(加密,再次)和点击次数等变量。

像这样的所有东西 可以 反向设计,所以考虑投入一堆垃圾数据,让人们摆脱气味。

编辑:在某些PHP会话中也可能值得一试。当他们点击开始游戏时开始会话,并且(如此帖子的评论所示)记录时间。当他们提交分数时,你可以检查他们是否实际上有一个开放的游戏,他们没有提交太快或太大的分数。

可能值得制定一个标量,看看说每秒/分钟的最高得分是多少。

这些东西都不是不可避免的,但它们有助于在Flash中找到一些人们可以看到它的逻辑。

3
Oli

根据我的经验,这最好是作为社会工程问题而不是编程问题。而不是专注于使其不可能作弊,而是通过消除作弊的动机来集中精力使其变得无聊。例如,如果主要激励是公开可见的高分,那么仅仅在显示高分时延迟可以通过去除欺骗者的正反馈循环来显着减少作弊。

3
Scott Reynen

您正在谈论所谓的“客户信任”问题。因为客户端(以现金形式,在浏览器中运行的SWF)正在做一些它旨在做的事情。保存高分。

问题是您要确保“保存分数”请求来自您的Flash影片,而不是某些任意HTTP请求。一种可能的解决方案是在请求时将服务器生成的令牌编码到SWF中(使用 flasm ),该请求必须伴随保存高分的请求。一旦服务器保存该分数,该令牌就会过期,不能再用于请求。

这样做的缺点是用户只能在每次加载flash电影时提交一个高分 - 你必须强制他们刷新/重新加载SWF才能再次播放新的分数。

2
Peter Bailey

一个新的流行街机mod的方式是它将数据从闪存发送到PHP,返回闪存(或重新加载),然后返回到PHP。这允许您执行任何您想要比较数据以及绕过后期数据/解密黑客等的操作。这样做的一种方法是将2个随机值从php分配到闪存中(即使运行实时闪存数据采集器也无法抓取或看到),使用数学公式将分数与随机值相加然后使用相同的公式,以反转它,看看当它最终进入最后的PHP时,得分是否匹配。这些随机值永远不可见,并且它也会发生交易次数,如果它超过几秒钟,那么它也会将其标记为作弊,因为它假定您已停止发送以试图找出随机值或运行通过某种类型的密码返回可能的随机值以与分数值进行比较。

如果你问我,这似乎是一个很好的解决方案,是否有人看到使用这种方法的任何问题?或者可能的方法呢?

2
Vaughn

我认为最简单的方法是每次游戏注册要添加的分数然后对其进行编码时调用RegisterScore(score)等函数,打包并将其作为字符串发送到php脚本。 php脚本会知道如何正确解码它。这将直接停止任何调用php脚本,因为任何强制分数的尝试都会导致解压缩错误。

2
mathieu landry

每当您的高分系统基于Flash应用程序通过网络发送未加入/未签名的高分数据这一事实时,可以截获并操纵/重放。答案如下:加密(正确!)或加密签名高分数据。这至少会让人们更难破解你的高分系统,因为他们需要从你的SWF文件中提取密钥。许多人可能会在那里放弃。另一方面,只需要一个单独的人来提取密钥并将其发布到某个地方。

真正的解决方案涉及Flash应用程序和高分数据库之间的更多通信,以便后者可以验证给定分数是否有些现实。这可能很复杂,取决于你有什么样的游戏。

2
Jan Krüger

没有办法使它完全不可攻击,因为它很容易反编译SWF,熟练的开发人员黑客可以跟踪你的代码并找出如何绕过你可能使用的任何加密系统。

如果你只是想通过使用像TamperData这样的简单工具来阻止孩子作弊,那么你可以生成一个加密密钥,你在启动时传递给SWF。然后使用 http://code.google.com/p/as3crypto/ 来加密高分,然后再将其传回PHP代码。然后在将其存储在数据库中之前在服务器端解密。

2
David Arno

我通常在高分条目中包含游戏会话的“幽灵数据”。因此,如果我正在制作赛车游戏,我会包含重播数据。你经常有重播数据的重播数据或幽灵赛车功能(与你的上一场比赛比赛,或者在排行榜上对抗花花公子#14的幽灵)。

检查这些是非常体力劳动,但如果目标是验证竞赛中的前10个条目是否合法,这可能是其他人已经指出的安全措施的有用补充。

如果目标是保持高分榜在线,直到时间的结束,没有任何人必须看他们,这不会带给你太多。

2
Lucas Meijer

只有将 所有游戏逻辑保留在服务器端 才可以在内部存储得分而不知道用户。出于经济和科学的原因,人类不能将这一理论应用于除回合制之外的所有游戏类型。对于例如将物理学保持在服务器端是计算上昂贵的并且很难以手的速度响应。甚至可能,在下棋时,任何人都可以将AI国际象棋游戏与对手相匹配。因此,更好 多人游戏 也应该包含按需创造力。

2
placeohlder

实现你想要的东西真的不可能。 Flash应用程序的内部总是可以部分访问,特别是当你知道如何使用像 CheatEngine 这样的东西时,意思是无论你的网站和浏览器服务器通信有多安全,它仍然会相对简单克服。

1
Mark Webster

通过 _ amfphp _ 与后端进行通信可能是个好主意。它应该阻止至少懒惰的人尝试通过浏览器控制台推送结果。

1
m1gu3l