Nodejs安全问题客户端发送请求

Nodejs Security issue client sending request

我正在创建一个浏览器游戏,我知道浏览器并不是很安全,但我想知道是否有解决我的问题的方法。

一个玩家杀了一个怪物,平台发送一个 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 倍的奖励 items/exp。

我考虑过在发送这个 reward 请求之前请求一个令牌,但这只会让事情变得更难,并没有真正解决任何问题。

您当前的设计

就目前情况而言,您在此处暗示的设计无法减轻您对游戏重放攻击的担忧。一种有用的思考方式是将其置于“会话式”HTTP request/response 范例的上下文中。目前,您允许的内容类似于:

  • 您的客户告诉您的服务器,“嘿,我赢得了这个奖励!”
  • 您的服务器无法验证这一点,因此响应:“太棒了!我已将该奖励添加到您的帐户中。”

如您所料,此范例无法提供太多保护,即使是稍微有点动机的攻击者也是如此。


目标设计

您的设计应该 强制执行的内容更符合以下内容:

  • 您的客户应该只能服务器请求某些东西。 ("我选择攻击怪物#123,结果如何?")
  • 您的服务器应该 响应 该请求,并提供与请求结果相关的信息。
    • “你不在那个敌人的射程内;你不能攻击他们。”
    • “那个敌人已经被你解决了。”
    • “那个敌人之前已经被其他玩家消灭了。”
    • “你不能在与另一个敌人战斗时攻击另一个敌人。”
    • “你没打中,敌人还有100hp。敌人反击打你,你现在有90hp。”
    • “你击中了敌人,敌人现在有1hp,敌人反击没打中,你还有100hp。”
    • “你击中了敌人,敌人现在0hp。你获得了5个金币作为奖励。”

在这种设计中,您的服务器将成为所有这些信息的看门人,尤其是攻击者试图修改的数据。您的服务器不会隐式地相信客户端没有被修改,而是自行计算所有这些,并在将这些计算结果记录在数据库或其他存储介质中后,简单地将这些计算结果发送回客户端以供显示。

供您考虑的其他项目

  1. 顺序标识符

    虽然您的服务器可能仍会协调所有这些操作并自行计算上述操作的结果,但任何有足够动机的攻击者仍然有可能找到利用您的后端的方法,因为您已经使用了可预测的增量值来识别您的后端实体。对您的端点进行少量研究并编写一个简短的脚本仍然可以成功地产生意想不到的游戏内财富。

    当创建您希望玩家无法枚举的实体时(阅读:可能是所有实体),您可以使用类似于 UUID 的东西来生成难以预测的独特身份标识。您的敌人现在在您的应用程序内部被称为 5e03e6b9-1ec2-45f6-927d-794e13d9fa8231e95709-2187-4b02-a521-23b874e10a03,而不是一个敌人是 #3130,下一个是 #3131。虽然根据数学定义,这些并不是可靠的加密安全,但这使得猜测标识符本身比连续整数要困难几个数量级。

    我通常允许我的数据库为我创建的每个实体自动生成 UUID,这样我就不必在后端实现中考虑它;开箱即用的支持将取决于您选择的 RDBMS。作为 SQL 服务器中的示例,您将 id 字段设置为类型 UNIQUEIDENTIFIER 并且默认值为 NEWID().

    如果您出于任何原因选择在 Node.js 中生成这些(如您所标记的),uuidjs/uuid 之类的东西可以为您生成这些。

  2. 速率限制

    您没有在您的问题中提及任何相关内容(无论您是否已经使用过),但您确实应该对您的端点的用户强制执行对您的游戏来说合理的速率限制。您的用户 JoeHacker 可以在一秒钟内攻击 15 次真的有道理吗?不,可能不是。

    完成此操作的方式因您使用的后端服务器而异 运行。对于基于 Express 的服务器,包括恰当命名的 express-rate-limit 在内的几个软件包将以相对简单但有效的方式完成工作。