6

我目前正在尝试建立一种技术来验证来自我的服务器的一些数据,用于一个简单的 Javascript 游戏。我知道尝试保护您的 Javascript 应用程序存在许多问题,因为根据定义,所有代码都可以在客户端使用。

例如,因为它是一个游戏,如果:

  var playerScore = 100

阻止人们简单地编辑 Javascript 并运行它......

  var playerScore = 10000000

...我将在服务器上验证它。然而,这(对我来说)似乎只是增加了一个非常微不足道的额外步骤来破解游戏。难道黑客\tweaker 不能将我的 Ajax 请求的目的地更改为他们自己的本地文件吗?

我考虑过可能会尝试比较客户端和服务器之间某些常见值(例如日期)的即时密钥\加密,但同样,加密方法将是可见的,因此避免使用微不足道。

我知道这是一个非常广泛的主题,但有没有人可以推荐的最佳实践?我看过这篇文章(防止 Javascript 游戏调整/黑客攻击),它有助于防止某些篡改,但我不确定如何验证正确的服务器端验证正在发生。

与往常一样,提前非常感谢。

4

2 回答 2

4

直接回答您的问题:

Q - “hacker\tweaker 不能将我的 Ajax 请求的目的地更改为他们自己的本地文件吗?”

A - 如果您的脚本没有被混淆,是的,他们可以更改 AJAX 请求目标以使用他们自己的游戏服务器。即使是经过混淆的脚本也可以被解包以更改黑客游戏的请求目的地,但他们这样做的含义是什么?他们将拥有自己的游戏版本,做自己的事情,与自己的服务器交谈……这几乎不是一个真正的问题,除非客户端游戏中有要解锁的秘密,拥有自己的服务器可能会使这些秘密更容易被发现只是阅读客户端源。

但是,为了保护游戏机密,游戏服务器可以在游戏期间将这些机密作为脚本有效负载丢弃……在这种情况下,您需要继续阅读。

- 我知道这是一个非常广泛的主题,但有没有人可以推荐的最佳实践?...我不确定如何验证正确的服务器端验证正在发生。

A - 为了防止游戏服务器上的游戏状态和统计数据的伪造,我不相信你在问这个......我相信你在问如何防止游戏客户端出现错误的游戏状态,哪个是不可能的,一旦客户端代码离开您的服务器,它就可能被篡改,并且任何防止措施也将包含在客户端代码中,并且本质上是可逆的,因此是可以规避的......

防止黑客攻击游戏服务器统计数据的对策

假设客户端是当前游戏分数的权威,并且服务器没有运行权威版本的游戏会话并从客户端接收用户输入更新(在这种情况下,用户输入只需要进行清理,但会引入复杂性该状态需要从服务器同步到客户端,并且对延迟问题很敏感)。

似乎这个问题通常没有完全缓解黑客攻击的完美解决方案,更不用说 JavaScript 游戏了,例如,在编译的 Flash 中,问题仍然很明显:阻止人们对 PHP 进行黑客攻击的最佳方法是什么- 基于 Flash 游戏的高分表,甚至在系统上的可执行级别:如何保护我的 .NET 程序集不被反编译?.

JavaScript 未编译并以纯文本形式发送,因此它没有这些障碍(可以绕过)黑客攻击。

我已经从链接的各种资源和一些谷歌搜索中编译了一种缓解这个问题的方法,我的游戏:

var score = 0;
function increaseScore() {
    score ++;
    sendScore();
}
function sendScore() {
    // Ajax
}
function run() {
    setTimeout(function() {
        increaseScore();
    }, 1000);
}
run();
  1. 闭包:在运行代码周围使用闭包将阻止函数和变量通过浏览器控制台被调用。它不会阻止断点或简单地修改脚本以删除闭包。

    (function() {
        var score = 0;
        function increaseScore() {
            score ++;
            sendScore();
        }
        function sendScore() {
            // Ajax
        }
        function run() {
            setTimeout(function() {
                increaseScore();
            }, 1000);
        }
        run();
    }) ();
    
  2. 混淆:您可以使用 Dean Edwards 打包程序,或者使用闭包编译器之类的东西简单地缩小代码。缩小不会阻止通过浏览器控制台观察和更改变量,但两者都会阻止源代码被人类阅读,这将是黑客找到可观察的合理断点所必需的。

    dean edwards 打包程序将脚本转换为经过 eval 处理的字符串,这使得无法在原始脚本中添加断点。但是,可以解压脚本并让黑客使用此脚本来添加断点。

    eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(1(){6 2=0;1 3(){2++;4()}1 4(){}1 5(){7(1(){3()},8)}5()})();',9,9,'|function|a|increaseScore|sendScore|run|var|setTimeout|1000'.split('|'),0,{}))
    
  3. 加密:客户端在发送前对分数进行加密,这不会阻止黑客在访问加密功能的情况下通过浏览器调试器修改分数,但会防止对服务器请求中的分数进行简单的伪造。

  4. 游戏实例令牌:服务器在请求中包含一个唯一令牌,该令牌在游戏开始时发送回服务器。这可以防止对脚本的简单修改(如解包或删除闭包),因为原始脚本已经使用了实例令牌,从而使其无效。

    确定的黑客将需要删除将令牌发回的代码以解决此问题,他们还需要确保从原始游戏实例中逻辑回传的分数(请参见步骤 6。并且变得更加复杂,因为第 5 步。)。

    (function() {   
        var score = 0;
        var token = "abcdef123456789"; // Inserted by server side code 
        function increaseScore() {
            score ++;
            sendScore();
        }
        function sendScore() {
            var encrypted = encryptScore();
            // Ajax send encrypted
        }
        function encryptScore() {
            // Uses some encryption lib
        }
        function sendToken() {
            // Ajax send token
        }
        function run() {
            sendToken();
            setTimeout(function() {
                increaseScore();
            }, 1000);
        }
        run();
    }) ();
    
  5. 评分令牌:服务器发送一个令牌,用于下一个要加密的分数。这减小了攻击窗口的大小。如果黑客找到了加密分数以发送的代码,他们仍然需要获取当前分数更新的正确令牌。

    (function() {   
        var score = 0;
        // Yes it would be easy to simply add items onto this queue
        // the closure and packing to prevent breakpoints, plus the
        // session token are required to prevent this
        var queue = [];
        var token = "abcdef123456789"; // Inserted by server side code 
        var sending = false;
        function increaseScore() {
            score ++;
            queue.push(score);
            if(!sending)
                sendScores();
        }
        // Scores now need to be sent sequentially, the last
        // score sent response will have the next scores token
        function sendScores() {
            sending = true;
            var encrypted = encryptScore(queue.shift(), token);
            // Ajax send encrypted, send next score when 
            // response received, if there is a next score 
            // in the queue, otherwise stop sending 
            // (sending = false)
        }
        function encryptScore(score, token) {
            // Uses some encryption lib
        }
        function sendToken(callback) {
            // Ajax send token, sets new token value from server response
        }
        function run() {
            sendToken(function() {
                setTimeout(function() {
                    increaseScore();
                }, 1000);
            });
        }
        run();
    }) ();
    
  6. 服务器端感知检查:服务器检查用户的分数是否增加了超过可能的程度,(或者在重复使用上述实例令牌的情况下甚至减少),这可能是顺序分数更新的步骤太大,不合逻辑或者分数在给定时间内增长过快。

  7. 黑名单:隔离显示作弊行为的用户和会话,这样黑客就不会发现他们已经被发现,并且他们的分数会从有效分数池中默默地删除。

  8. 灰名单:标记看起来非常接近完美评分场景的用户或游戏会话,以供手动调查以决定是否列入黑名单。

一般来说,我会创建尽可能多的审计数据来尝试监控情况,在必要时进行手动干预,并在实际情况再次出现问题时添加更多预防措施。

此外,由于这个问题没有完美的解决方案,它只是让它变得尽可能困难,不值得花时间破解它。总是有顽固的黑客准备迎接挑战,但要保持正确的态度:如果黑客确实尝试使用当前的对策来破解分数,结果会是什么?这值得你花时间规避吗?

猜测商业游戏制作者会使用书中的大部分技巧,但即便如此,这也取决于预算与风险。

于 2013-05-19T11:22:17.247 回答
1

我认为您的第一步是对用户隐藏 javascript 变量。

将游戏变量包装在 javascript 闭包中,因此无法从外部访问它们。

var gameSettings = {};
(function () {
    var playScore = 100;

    $.get("/Game/UserPlayScore", { userId: 'userId' }, function (data, status) {
        if (status === "success") {
            playScore = data.playScore;
        }
    });

    gameSettings.userPlayScore = function() {
        return playScore;
    };
}());

// playScore is unaccessible from outside the function
// but you can read the value by calling gameSettings.userPlayScore();
于 2013-05-19T11:33:02.727 回答