保护您的API nodejs免受攻击重做[第2部分]
#安全 #node #api #hacking

好!
在本文的第一部分中,我们看到了什么是正则表达式,什么是规则。如果您尚未访问您的这一部分,以在本文第二部分的前进之前进行背景化。
要访问它,请单击AQUI
跟我来,现在我们将解决这个问题ð

缓解风险
美丽!我们知道什么规则以及他如何完成,现在让我们看看我们能做什么来解决这种情况。

有一些可能的解决方案,例如验证正则表达式以查看它是否脆弱。一部分很难100%确保给定表达式是100%安全的,因为此攻击是基于用户发送的条目,并且有无限的可能性。

我们还可以尝试限制输入的大小,但这也无效,有一些区域模式可以锁定3个或更小的字符串!那么该怎么办?简单!
让我们使用Micro VM在主线程之外的另一个线程上编译正则条件的区域,以防我们无法评估的有效负载,我们的应用程序不会受到损害。

,但足以说话!跟我来,我将在实用中向您展示。

测试项目
为了测试这种情况,我们将使用使用Express Minimalist框架在NODEJS中构建的简单API。
有关Express AQUI的更多信息。
将是一个API,必须使用正则表达式验证电子邮件和密码的格式。
它有3个端点,为:

/validate-form-unsafe :此端点使用用于进行验证的 *表单。将用于显示这可能对系统产生负面影响。

/验证形式安全:该终点将用于测试我们的解决方案的建议是否有效。

/test-server :此端点将用于测试服务器的响应能力。

记住这只是一个示例API,所以有些事情可以做得更好,但是我们不会专注于此,好吗?
如果要下载或进行替换克隆,请单击AQUI
在我们的示例中,我们将使用以下言论:

/^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/"gm"

如果字符串日期是一封徒劳的电子邮件,而其他常规的目的是验证其目的:

/^(([a-z])+.)+[A-Z]([a-z])+$/

用于验证知情密码是否以点为点格式,例如“ a.b.c.zz”或“ ab.cc.zd”

睾丸
当我们测试端点/validate-form-unsafe 以徒劳的条目,例如:

{
    "email":"emailvalido@gmail.com",
    "password":"a.b.Cz"
}

一切正常流动。
porti©m如果我们发送恶意有效载荷,例如:

{
    "email":"email-valido@gmail.com",
  "password":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}

这就是发生的事情:

Server not responding
咨询端点/test-Service 我们发现我们的API根本不起作用。

server offline

但是发生了什么?
好吧,让我们看看答案的答案。使用正则验证错误方式的功能,以下:

function unsafeValidateForm(email, password) {
  const isEmailValid = validadeEmailRegex.exec(email) !== null;
  const isPasswordValid = validadePasswordRegex.exec(password) !== null;
  return isEmailValid && isPasswordValid;
}
这些表达式在问题的主线程中进行了评估。这样,由于灾难性评估(灾难性评估)的行为,灾难性的回溯(CATASTRIAN RETORT) nodejs停滞不前,能够促进,他可以这样做,他可以做到这一点无法达到Eventloop的其他阶段,无法处理到达的请求,从而导致服务中断。记住我解释了更多有关eventloop是什么的细节,您的阶段是什么,为什么我们在我的其他文章中完全无法阻止它,从dev.to,clique aqui para acessá-lo

soluã§o
好吧,我们该怎么办才能解决这个问题?
让我们使用一个非常酷的功能,即节点VM的
如果您不知道这是什么,这里有一个简洁的解释:

vm 是一种机制仍执行,但能够访问和修改本地变量的能力。每个VM都有自己的执行线程,独立于主线程执行。

Mais sobre vm's

接下来,我们具有密码验证功能的修改版本。

async function safeValidateForm(email, password) {
  try {
    validateData(email, password);

    const vmVariables = {
      regexResult: false,
    };

    const context = vm.createContext(vmVariables);

    const regexScript = new vm.Script(
      `regexResult = new RegExp(${validadePasswordRegex}, "gm").exec("${password}") !== null &&
     RegExp(${validadeEmailRegex},"gm").exec("${email}") !== null`
    );

    regexScript.runInContext(context, {
      timeout: maxRegexEvaluationTimeoutInMillissecons,
    });
    return vmVariables.result !== null;
  } catch (error) {
    if (error.code == InternalErrorMessages.TIMEOUT) {
      throw new TimeoutError("ValidateForm");
    }
    throw error;
  }
}

这种方法是要在线程分开上执行此检查的原理,因此我们在此处使用VM为此,因此我们没有阻塞主线程的危险,因此导致API的完整锁定。

让我们看一下新功能并检查其功能。早期,我们有以下摘录来自Córib:

  try {
    validateData(email, password);

    const vmVariables = {
      regexResult: false,
    };

   const context = vm.createContext(vmVariables);

在这里,我们正在创建一个新的执行环境,要在我们将使用的VM中使用。如果我们想将任何值从当前上下文传递给VM,则在此阶段,我们应该在上下文中插入我们要注入的信息中创建的信息。

!!!!

从用户收到的输入永不,应直接传递给系统,而无需先接受治疗。 验证的函数我在这里做的是空的,因为这只是为了提醒您,在生产时,您必须始终处理收到的输入,好吗?
在这里,我不会坚持这一点,因为焦点是另一个。

接下来我们有以下内容:

  const regexScript = new vm.Script(
    `regexResult = new RegExp(${validadePasswordRegex}, "gm").exec("${password}") !== null &&
     RegExp(${validadeEmailRegex},"gm").exec("${email}") !== null`
  );

在这里,我们转到字符串VM将执行的脚本。请注意,此脚本正是对我们需要的正则表达式的评估,正是电子邮件 password 我们需要评估的常规和用户​​。<<<<<<<< br> 请注意,在脚本中,我们有以下摘录“ RegexResult = new Re ...”此 RegexResult 与我们在上下文中声明的相同,并且将获得结果的值我们的操作£o。当处理此微VM结束时,我们可以通过执行 vmvariables.regexresult 来访问此结果。 ð

最后我们有成本:
摘录

 regexScript.runInContext(context, {
      timeout: maxRegexEvaluationTimeoutInMillissecons,
    });
    return vmVariables.result !== null;
  } catch (error) {
    if (error.code == InternalErrorMessages.TIMEOUT) {
      throw new TimeoutError("ValidateForm");
    }
    throw error;
  }

在这里,我们使用创建的上下文在Micro VM中执行上方创建的脚本,我们可以访问Regex的执行结果 vmvariables.regxResult.regxResult
这里重要的是要注意超时属性< /strong>,这正是使我们能够继续处理事物的原因,即使此微VM结束,因为它达到 maxregevaluction millincons < / Strong>,在我们的情况下为50配置,VM已死亡,并且启动错误。此错误在捕获块中启动并捕获,我们对此进行了处理,引发了个性化的超时错误。

基本上,这就是我们可以对待常规的方式,我们不知道我是否会锁定或不锁定,甚至可能损害我们的应用程序。

现在,我们已经看到了如何解决这个问题,让我们测试解决方案,看看它是否确实有效。
但这是第3部分,因为这里有很多信息。在下面,您将找到指向本文的第三部分也是最后一部分的链接。我希望你能。

TerceiraParte