阻止人们攻击Flash游戏基于PHP的高分表的最好方法是什么?

我正在谈论一个没有高分的动作游戏,没有办法通过回放动作来validation服务器上的分数。

我真正需要的是在Flash / PHP中最强大的encryption,以及防止人们通过我的Flash文件调用PHP页面的方法。 过去曾经尝试过一些简单的方法来进行一个单独的分数的多个调用,完成一个校验和/斐波那契序列等,也用Amayeta SWF Encrypt混淆了SWF,但最终都被黑掉了。

感谢StackOverflow响应我现在从Adobe发现了一些更多信息 – http://www.adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html和https://github.com/mikechambers/as3corelib – 我想我可以用于encryption。 不知道这会让我在CheatEngine周围。

如果两者不同,我需要知道AS2和AS3的最佳解决scheme。

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

这是networking游戏和竞赛的一个经典问题。 你的Flash代码和用户一起决定一个游戏的分数。 但用户不可信,并且Flash代码在用户的计算机上运行。 你是SOL。 没有什么可以做的,以防止攻击者造成高分:

  • Flash比你想像的更容易反向工程,因为字节码有很好的文档和描述高级语言(Actionscript)—当你发布一个Flash游戏时,你会发布你的源代码,无论你知道与否。

  • 攻击者控制Flash解释器的运行时内存,以便任何知道如何使用可编程debugging器的人都可以随时更改任何variables(包括当前分数),或更改程序本身。

对您的系统最简单的攻击是通过代理运行游戏的HTTPstream量,抓取高分保存,并以更高的分数重播。

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

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

(你也可以使用会话cookie来达到同样的效果)。

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

所以接下来你不仅要提供一个令牌或会话cookie,而且要提供一个高分数的encryption会话密钥。 这将是一个128位的AES密钥,本身使用硬编码到Flash游戏的密钥进行encryption:

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

现在在游戏发布高分之前,它解密高分encryption会话密钥,它可以这样做,因为您将高分encryption会话密钥解密密钥硬编码到Flash二进制文件中。 您使用此解密密钥和高分的SHA1哈希encryption高分:

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

服务器上的PHP代码检查令牌以确保请求来自有效的游戏实例,然后解密encryption的高分,检查以确保高分与高分的SHA1匹配(如果跳过此步骤,解密只会产生随机的,可能非常高,高分)。

所以现在攻击者反编译你的Flash代码,并迅速findAES代码,这样就像拇指一样伸出手,虽然即使没有,也会在15分钟内用内存search和跟踪器(“我知道我的这个游戏的分数是666,所以让我们在内存中find666,然后抓住任何接触这个值的操作—哦,看,高分encryption代码!“)。 使用会话密钥,攻击者甚至不必运行Flash代码; 她抓取一个游戏启动令牌和一个会话密钥,并可以发回任意高分。

您现在正处于大多数开发人员放弃的地步 – 通过以下方式给攻击者带来或者需要花费几个月的时间:

  • 用XOR操作encryptionAES密钥

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

  • 在整个二进制文件中散布伪造的密钥encryption和高分。

这大都是浪费时间。 不言而喻,SSL也不会帮助你; 当两个SSL端点之一是邪恶的时,SSL不能保护你。

这里有一些事实上可以降低高分欺诈:

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

  • 拒绝游戏中的最高分数,这个分数比有史以来最短的实际游戏less得多(对于更复杂的方法,尝试“隔离”高于平均游戏持续时间2个标准差的游戏分数)。 确保你正在跟踪服务器的游戏时间。

  • 拒绝或隔离仅login游戏一次或两次的高分,以便攻击者必须为每个创build的login创build合理的游戏玩法的“纸质线索”。

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

  • 游戏过程中的“快照”游戏状态(例如弹药数量,等级位置等),您可以稍后与logging的中间分数进行对比。 您甚至不必有办法检测这些数据中的exception情况; 你只需要收集它,然后你可以回去分析它,如果事情看起来很腥。

  • 禁用任何安全检查失败的用户帐户(例如,通过提交未经validation的encryption高分)。

记住,你只是在这里阻止高分的欺诈行为。 没有什么可以做,以防止如果。 如果游戏中有钱,那么有人会打败你提出的任何系统。 目的不是要阻止这种攻击; 这是为了让攻击更加昂贵,而不仅仅是让游戏变得更好,并且击败游戏。

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

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

一个简单的方法来做到这一点,将提供你的高分值的密码散列,以及它自己的分数。 例如,当通过HTTP GET发布结果时: http : //example.com/highscores.php? score= 500& checksum= 0a16df3dc0301a36a34f9065c3ff8095

计算这个校验和时,应该使用一个共享的秘密; 这个秘密不应该通过networking传输,而应该在PHP后端和Flash前端中进行硬编码。 上面的校验和是通过在string“ secret ”前加上“ 500 ”来创build的,并通过md5sum来运行。

虽然这个系统将阻止用户发布任意分数,但是这并不妨碍“重放攻击”,即用户重新发布之前计算的分数和散列组合。 在上面的例子中,500的分数总是会产生相同的哈希string。 这些风险中的一些可以通过在要被散列的string中包含更多的信息(例如用户名,时间戳或IP地址)来缓解。 虽然这不会阻止数据的重播,但它将确保一组数据仅对单个用户一次有效。

为防止发生重播攻击,必须创build一些types的质询 – 响应系统,如下所示:

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

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

或者,通过强制客户端通过HTTPS与服务器通信,确保客户端已预先configuration为仅信任由您独自访问的特定证书颁发机构签发的证书。

我喜欢tpqf所说的,而不是在发现作弊时禁用帐户,而是在每次login时执行一个蜜jar,他们看到他们的黑客分数,从不怀疑他们被标记为巨魔。 谷歌的“phpBB MOD巨魔”,你会看到一个巧妙的方法。

在接受的答案tqbf提到,你可以做一个内存search得分variables(“我的分数是666,所以我在内存中寻找数字666”)。

有一个办法解决这个问题。 我在这里有一个类: http : //divillysausages.com/blog/safenumber_and_safeint

基本上,你有一个对象来存储你的分数。 在设置器中,它将你传递给它的值乘以一个随机数(+和 – ),并且在getter中用随机乘数除以保存的值,以获得原始值。 这很简单,但有助于停止记忆search。

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

使用已知(私有)可逆密钥进行encryption将是最简单的方法。 我并不是所有的,所以我不知道什么样的encryption提供者。

但是你可以包含游戏长度(encryption,再次)和点击次数等variables。

所有这样的事情都可以进行逆向工程,所以可以考虑扔掉一堆垃圾数据,把人们扔掉。

编辑:这可能是值得在一些PHP会议夹。 当他们点击开始游戏时(和这个post的评论说的)logging时间,开始会话。 当他们提交分数时,你可以检查他们实际上是否有一个开放的游戏,他们不太快或太大提交分数。

可能值得用标量来看看每秒/分钟的最高分数。

这些事情都不是不可避免的,但是这有助于在闪存中有一些逻辑,人们可以看到它。

根据我的经验,这最好是作为一个社会工程问题而不是一个编程问题。 不要把注意力放在不可能作弊上,而是要消除欺骗的诱因,把注意力放在无聊之上。 例如,如果主要奖励是公开可见的高分,则简单地延迟高分显示可以通过去除作弊者的正反馈循环而显着减less作弊。

每当您的高分系统基于Flash应用程序通过networking发送未识别/未识别的高分数据的事实,可以拦截和操纵/重播。 答案如下:encryption(体面!)或密码签名高分数据。 这至less会让人们难以破解高分系统,因为他们需要从SWF文件中提取密钥。 很多人可能会放弃在那里。 另一方面,只需要一个人把密钥提取出来并放在某个地方。

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

由于易于反编译SWF,所以没有任何办法让它变得完全不可靠,而熟练的开发者黑客可以跟踪你的代码,找出如何绕过你可能使用的任何encryption系统。

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

你在谈论什么叫做“客户端信任”问题。 因为客户(在这个现金中,一个在浏览器中运行的SWF)正在做一些devise工作。 保存高分。

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

这样做的缺点是,用户只能提交一个高负荷的Flash影片 – 你必须强制他们刷新/重新加载SWF才能重新播放新的乐谱。

我通常包含高分条目的游戏会话的“鬼数据”。 所以如果我正在制作一款赛车游戏,那么我会包含重播数据。 你经常有重放数据的重放function或幽灵赛车function(对你的最后一场比赛,或对排行榜上#14的哥们玩鬼)。

检查这些是非常体力劳动的,但是如果目的是validation比赛中前十名的参赛作品是否合法,这可能是其他人已经指出的安全措施的有益补充。

如果目标是保持高分榜上网,直到时间的尽头,没有人看他们,这不会给你带来太多。

新的stream行街机模式的方式是,它发送数据从闪存到PHP,回到闪存(或重新加载),然后回到PHP。 这可以让你做任何你想要比较的数据,以及绕过数据/解密黑客等。 其中一种方法是将2个随机值从php分配到闪存中(即使运行实时Flash数据采集卡,也无法捕获或查看),使用math公式将随机值添加分数,然后使用检查相同的公式来反转它,看看它的分数是否匹配它,当它最终到达PHP的时候。 这些随机值是不可见的,它也是交易发生的时间,如果它超过了几秒钟,那么它也会将其标记为欺骗,因为它假定您已经停止发送以尝试计算随机值或运行数字通过某种types的密码返回可能的随机值与分数值进行比较。

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

我认为最简单的方法就是每次游戏注册一个要添加的分数,然后对它进行编码,打包并将其作为string发送到php脚本,就可以调用像RegisterScore(分数)这样的函数。 PHP脚本将知道如何正确解码。 这将停止任何调用直接到PHP脚本,因为任何试图强制分数将导致解压错误。

只有将所有的游戏逻辑保存在服务器端 ,也可以在不知道用户的情况下在内部存储分数。 由于经济和科学的原因,人类不能将这个理论应用于除回合之外的所有游戏types。 例如,在服务器端保持物理的计算成本很高,很难以手的速度得到响应。 甚至有可能,在下棋的同时,任何人都可以将AI棋游戏与对手相匹配。 因此,更好的多人游戏也应该包含按需创意。

你不能相信客户端返回的任何数据。 validation需要在服务器端执行。 我不是一个游戏开发者,但是我制作商业软件。 在这两种情况下,都可能涉及金钱,人们将破坏客户端混淆技术。

也许定期发送数据到服务器,并做一些validation。 不要专注于客户端代码,即使这是您的应用程序的生命。

我做了一种解决方法…我给了一个增加分数(你总是得到+1分)。 首先,我从随机数(比如说14)开始计数,当我显示分数时,只显示了分数减去14.这就是如果cookies正在寻找20的例子,他们不会find它(它将在内存中为34)。 其次,因为我知道下一点应该是什么…我使用Adobeencryption库来创build下一个点应该是什么的散列。 当我必须增加分数时,我检查增加分数的哈希是否等于哈希应该是。 如果黑客已经改变了内存中的点,那么哈希值就不相等。 我执行一些服务器端validation,当我从游戏和PHP获得不同的观点时,我知道涉及作弊。 这里是我的代码片段(我使用Adobe Crypto libraty MD5类和随机encryption盐。callPhp()是我的服务器端validation)

 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,我能够阻止cookies。 另外,当我将分数发送到服务器端时,我使用自己的小型encryption/解密函数。 像这样的东西(服务器端代码不包括在内,但你可以看到algorithm,并用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 } 

然后我把这个variables和其他的虚假variables一起发送出去,在这个过程中就会迷失方向了。对于一个小型的Flash游戏来说,这是一个很大的工作,但是涉及到奖品的时候,有些人会变得贪婪。 如果您需要任何帮助,请给我写信。

干杯,Ico

真的不可能达到你想要的。 Flash应用程序的内部部分总是可以部分访问的,特别是当您知道如何使用CheatEngine时 ,意味着无论您的网站和浏览器服务器通信如何安全,它仍然会相对简单地克服。

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