【问题标题】:Does node.js create a greater risk of data being shared across connections?node.js 是否会增加跨连接共享数据的风险?
【发布时间】:2014-04-04 18:46:59
【问题描述】:

由于 node.js 是单线程的,并且许多请求使用异步回调或事件并行运行,并且任何一个请求都不会立即运行完成,因此一个 http 请求的进程最终会与其他进程共享变量是否存在风险请求,尤其是在函数/模块内的临时对象或变量中存储数据时?

例如,在 php 中,每个进程都有自己的线程并运行到完成(阻止其他任何内容)。因此,无法跨连接/请求访问变量。

我尝试了谷歌搜索,但没有找到太多。这甚至是一个问题吗?变量是否可能在来自不同(甚至相同)用户的单个请求之间无意共享?

【问题讨论】:

    标签: node.js security


    【解决方案1】:

    只有一种情况可能会引起关注:无意的全局变量。

    考虑以下 Express 路线:

    app.post('/foo', function(req, res) {
        var user = req.cookies.user;
        token = generateToken(user);
    
        someAsyncOperation(user, req.body, function(err, result) {
            saveUser(token, result);
            res.send(result);
        });
    });
    

    你看到错误了吗? user 是函数的作用域,但 token 是一个隐式全局变量,将在所有请求之间共享。

    在此示例中,假设作者可能打算在 user 的定义之后使用逗号而不是分号(这将导致 token 的范围正确)。不幸的是,他的手指向东北方向下降了一厘米,造成了一个容易错过的错误。

    • 请求 A 进来。user 是从 cookie 加载的,并且某种令牌生成函数会创建一个对用户来说应该是唯一的令牌。
    • 一个异步操作排队,回调函数绑定到请求A的范围内。
    • 异步操作需要一些时间,因此节点处于空闲状态。
    • 请求 B 进来。它在其范围内初始化一个新的 user
    • 全局 token 被请求 B 的令牌覆盖
    • 另一个异步操作排队等待 B;节点再次空闲。
    • A 的异步操作完成。
    • 呃-哦。 A 操作的结果使用 B 的令牌保存,因为这是 token 上次设置的值。
    • B 的异步操作完成并将更多数据保存到 B 的令牌中。

    在这种情况下,A 中没有保存任何数据,而 B 中保存了两组数据。这可能导致从几乎不可能重现和调试的奇怪不一致到实际的财务损失。 (我是不是不小心给B发了两个订单?)

    好消息是很容易避免这个问题。使用strict mode。严格模式将隐式全局变量赋值为 ReferenceError 等。只需添加到您的 js 文件的顶部:

    'use strict';
    

    或者使用参数运行节点:

    --use_strict
    

    启用严格模式后,您不会意外使用隐式全局,因此这不再是问题。局部变量和req/res 对象是安全的;唯一可能遇到麻烦的其他领域是当您有意使用某种共享状态时,但在任何其他语言中存在相同的危险。

    【讨论】:

    • ...我既不能确认也不能否认自己犯了这件事。
    【解决方案2】:

    Javascript 的工作方式与 PHP 不同。范围的行为是完全不同的,并且是防止问题成为问题的原因。

    现实世界的类比是假设您有许多工作台排成一列。你的老板进来,在长凳上放了一些要处理的东西。当你在处理它时,他会在其他长凳上放置更多物品。

    在某个时间点,您到达 1 号桌的停靠点,您将不得不等待。您移动到 ​​2 号办公桌开始在那里工作。一旦你看到 #1 号桌再次准备就绪,你就去 #1 号桌继续工作。由于您的办公桌完全不同,因此很难混淆。你只需要记住不要随身携带东西。 Node 中的每个函数都有完全不同的作用域,这使得 node 很难混淆它正在做什么。

    function object_init() { this.a = 'a'; return this }
    
    function server_resp(req, res) {
        var obj = object_init();
        if ( req.changeTrue() ) {
             obj.a = 'b';
        }
        function test() {
            longOperation();
            console.log( obj.a );
    
        }
    
        test()
    }
    

    上面的 sn-p 是 Node.js 应用程序中可能发生的非常高级别的正常 sn-p。大多数 javascript 应用程序都会有一个与此非常相似的 sn-p 代码。

    您担心的是,在 longOperation() 之后,Node 将返回到错误的 server_resp 实例。它没有发生的原因是因为每个请求都有自己的“板凳”。 Node 足够聪明,可以从一个长凳移动到另一个长凳,而无需将物品从一个长凳移动到另一个长凳。 Node 的工作是执行不进位。每个“长凳”都有自己的 obj。每个“长凳”甚至都有自己的 test()。这就是 Javascript 的工作方式,因为函数与对象密切相关。

    PHP 具有与 javascript 完全不同的内存和执行模型,这就是为什么 javascript 可以安全地做到这一点。 PHP 更程序化/OOP,而 javascript 更具有功能性。

    这是对节点如何工作的一般解释。表面之下可能潜伏着安全漏洞。根据内存模型和大小检查,整数溢出可能会重写内存库,并使攻击者能够访问他们以前无法访问的范围。糟糕的代码可能会扩大这个漏洞。对于普通代码,不同请求之间的混淆几乎是不可能实现的。

    【讨论】:

      【解决方案3】:

      如果你做得对,这并不是一个真正的问题,我并不是说轻率。 :-) 大多数语言都有全局范围或共享/可共享的数据结构。即使使用线程操作,这也可能是一个风险,例如在线程环境中不正确地使用/实现单例。但是,如果您花时间了解 Javascript 的工作原理,并以反映这种理解的方式编写代码,那么这真的不成问题。

      我建议您不要在搜索结果中出现太多关于它的讨论,因为网络上有很多关于 javascript 的内容,应该让您放心,这并不是什么大问题问题。

      花一些时间来了解(通过阅读和一些实验)范围和闭包在 Javascript 中的工作原理,您应该对此感到更加自在。

      this question 上的前两个评分答案应该有助于理解范围和闭包。除此之外,查看一些基于 Express 构建的各种示例应用程序(并深入研究 Express 和 Connect 的代码)并了解它们是如何实现的。

      【讨论】:

        猜你喜欢
        • 2012-09-16
        • 1970-01-01
        • 2021-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-28
        • 1970-01-01
        • 2015-08-27
        相关资源
        最近更新 更多