问题描述
我正在创建一个浏览器游戏,我知道浏览器不是很安全,但我想知道是否有任何解决方法可以解决我的问题。
一个玩家杀死了一个怪物,平台向我的后端发送一个 ID,如下所示:
Axios({
url: this.server + "reward",headers: { token: "foo123",charToken: "bar123" },method: "POST",data: {
id: 10001,// Monster ID
value: 10 // How many monsters were killed
},});
问题是,我认为没有办法阻止用户只是向服务器发送随机请求,说他实际上在一秒钟内完成了 300 次此关卡并获得了 300 倍的奖励项目/经验。
在发送此 reward
请求之前,我曾考虑过请求令牌,但这只会让它变得更难,并没有真正解决任何问题。
解决方法
您当前的设计
就目前而言,您在此处暗示的设计无法减轻您对游戏重放攻击的担忧。考虑这一点的一种有用方法是在“对话式”HTTP 请求/响应范式的上下文中构建它。目前,您允许的内容类似于:
- 您的客户告诉您的服务器,“嘿,我赢得了这个奖励!”
- 您的服务器无法验证这一点,回复说:“太好了!我已将该奖励添加到您的帐户中。”
正如您所猜想的那样,这种范式即使对有轻微动机的攻击者也没有提供太多保护。
目标设计
您的设计应该强制执行以下内容:
- 您的客户应该只能向服务器请求某些事情。 (“我选择攻击怪物#123,结果如何?”)
- 您的服务器应响应该请求并提供与该请求结果相关的信息。
- “你不在那个敌人的范围内;你不能攻击他们。”
- “那个敌人已经被你消灭了。”
- “那个敌人之前已经被另一个玩家派遣了。”
- “当你与另一个敌人战斗时,你不能攻击另一个敌人。”
- “你没击中,敌人还有 100hp。敌人还击打你;你现在有 90hp。”
- “你击中了敌人,敌人现在有 1hp。敌人还击未命中;你还有 100hp。”
- “你击中了敌人,敌人现在 0hp。你已经获得了 5 个硬币作为奖励。”
在这种设计中,您的服务器将成为所有这些信息的看门人,尤其是攻击者试图修改的数据。您的服务器不会隐含地相信客户端没有被修改,而是会自行计算所有这些,并在将这些计算结果记录到数据库或其他存储介质中后,简单地将这些计算结果发送回客户端以供显示。>
其他供您考虑的项目
-
顺序标识符
虽然您的服务器可能仍会协调所有这些操作并自行计算所述操作的结果,但任何有足够动机的攻击者仍有可能找到利用您的后端的方法,因为您已使用可预测递增的值来识别您的后端实体。对您的端点进行少量研究并编写一个简短的脚本,仍然可以成功地产生意想不到的游戏内财富。
创建您希望玩家无法枚举的实体(阅读:可能是所有实体)时,您可以使用类似于 UUID 的东西来生成难以预测的唯一标识符。现在,您的敌人在您的应用程序内部被称为
5e03e6b9-1ec2-45f6-927d-794e13d9fa82
和31e95709-2187-4b02-a521-23b874e10a03
,而不是一个敌人是 #3130,下一个是 #3131。虽然根据数学定义,这些在加密方面并不可靠,但这使得猜测标识符本身比连续整数要困难几个数量级。我通常允许我的数据库为我创建的每个实体自动生成 UUID,因此我不必在后端实现中考虑它;对此开箱即用的支持将取决于您选择的 RDBMS。以 SQL Server 中的示例为例,您将
id
字段设置为类型UNIQUEIDENTIFIER
并且默认值为NEWID()
。如果您出于任何原因选择在 Node.js 中生成这些(正如您所标记的那样),诸如
uuidjs/uuid
之类的东西可以为您生成这些。 -
速率限制
您在问题中没有提及任何相关内容(无论您是否已经使用过它),但您确实应该为您的终端用户强制执行一个对您的游戏合理的速率限制。您的用户
JoeHacker
在一秒钟内攻击 15 次真的可信吗?不,可能不是。完成此操作的方式因您运行的后端服务器而异。对于基于 Express 的服务器,包括名称恰当的
express-rate-limit
在内的多个软件包将以相对简单而有效的方式完成工作。