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

Leo*_*rra 0 javascript rest ddos rate-limiting node.js

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

玩家杀死怪物,平台将 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
   },
});
Run Code Online (Sandbox Code Playgroud)

问题是,我认为没有办法阻止用户向服务器发送随机请求,说他实际上在一秒钟内完成了这个关卡 300 次,并获得了 300 倍的奖励物品/经验。

我想过在发送这个reward请求之前请求一个令牌,但这只会让事情变得更困难,而且并不能真正解决任何问题。

esq*_*qew 5

您当前的设计

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

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

正如您所猜测的,即使是稍微有动机的攻击者,这种范例也无法提供太多的保护。


目标设计

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

  • 您的客户端应该只能向服务器请求某些内容。(“我选择攻击怪物#123,结果如何?”)
  • 您的服务器应该使用与该请求结果相关的信息来响应该请求
    • “你不在那个敌人的射程之内;你无法攻击他们。”
    • “那个敌人已经被你解决掉了。”
    • “那个敌人之前已经被其他玩家干掉了。”
    • “当你与另一个敌人战斗时,你无法攻击另一个敌人。”
    • “你没击中,敌人还有100生命值。敌人反击击中你,你现在有90生命值。”
    • “你击中了敌人,敌人现在有1点生命值。敌人反击没有击中,你还有100点生命值。”
    • “你击中了敌人,敌人的生命值为0。你获得了5个金币作为奖励。”

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

其他供您考虑的事项

  1. 顺序标识符

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

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

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

    如果您出于某种原因选择在 Node.js 中生成这些(如您标记的那样),类似的东西uuidjs/uuid可以为您生成这些。

  2. 速率限制

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

    完成此操作的方式根据您运行的后端服务器而有所不同。对于基于 Express 的服务器,包括适当命名的多个软件包express-rate-limit将以相对简单但有效的方式完成工作。