【问题标题】:Node.js client certificate authentication on some paths only仅在某些路径上的 Node.js 客户端证书身份验证
【发布时间】:2013-01-06 15:39:25
【问题描述】:

我有一个基于 node.js 的 Web 应用程序,它需要来自客户端的安全 (https) 连接。我想要的是在某些路径上需要客户端证书身份验证,而在其他路径上则不需要。

例如。如果用户转到https://www.example.com/main,则服务器不需要客户端证书身份验证(因此浏览器不会询问任何内容)。但是如果用户导航到https://www.example.com/secure,则需要客户端证书身份验证(因此浏览器会弹出一个对话框选择使用哪个证书)。

我怎样才能做到这一点。如果我将requestCert:truerejectUnauthorized:true 传递给https.createServer 选项,我可以强制客户端证书身份验证。这种方法的问题是每个路径都需要客户端证书。

【问题讨论】:

  • 客户端证书是 SSL 握手的一部分;我不确定你能做你想做的事。
  • @Joe:这就是我所害怕的。但我见过这样做的网站。首先,这些应用程序会显示一个页面,要求用户插入智能卡(客户端证书所在的位置)。当您按“确定”时,应用程序会将用户重定向到同一应用程序中需要客户端证书的另一个路径。此时浏览器会显示一个对话框供用户选择使用的证书。那么,也许有办法在同一个连接中强制进行新的握手?
  • 我有办法解决这个问题,但我不想使用它。一种解决方案是在同一个应用程序中使用两个不同的侦听器(不同的端口)。另一个需要客户端证书身份验证,另一个不需要。这种方法的问题是我必须为第二种方法使用非标准端口。这导致一些客户需要重新配置他们的防火墙以允许连接到该端口。
  • @Jukka:你找到单独监听器的替代方案了吗?
  • 也许是npmjs.com/package/client-certificate-auth——虽然我自己没有测试过。

标签: node.js ssl https x509


【解决方案1】:

我来到这里是因为我正在处理同一个问题,并将分享我正在采取的方法。对于要求用户使用有效证书进行身份验证的端点,我使用附加到需要证书的端点的自定义路径,并验证用户是否已使用证书进行身份验证,如果他们正在访问遵循该路径的任何端点(假设为 express 应用程序)。即:

app.use('/api/cert', function(req, res, next){
    //validate that any token passed in the request was generated for a valid
    //cert login, otherwise reject request

    //pseudo-code
    if(isValidCertToken(req.authentication.token)) {
        next(); //this will pass it on to the correct endpoint in the /api/cert/[whatever] chain
    } else {
        req.status(401).send({error: 'invalid login, cert required'});
    }
});

这将要求您允许用户通过将 requestCert 标志设置为 true 来使用证书进行身份验证,但如果用户不希望使用“需要证书”的端点,也将允许其他身份验证方法因为在设置 https 连接时 rejectUnauthorized 设置为 false。

【讨论】:

  • 这仍然意味着提示用户在每条路径上选择哪个证书
  • 很遗憾。我们通过使用 OAuth 2.0 的独立 SSO 服务器来避免这种情况,我们重定向到该服务器进行身份验证。流程:应用程序->[用户单击登录]->直接到 OAuth 2.0 服务器->[用户提示输入证书]->[用户根据证书验证]-> 使用代码重定向到应用程序以请求令牌或应用程序->[用户点击登录]->直接到 OAuth 2.0 服务器->[用户提示输入证书]->[用户选择取消无证书]->显示登录屏幕->[使用 UN/PW 验证的用户]->使用代码重定向到应用程序请求令牌
【解决方案2】:

我认为这个问题可以通过使用Node.js包http-proxy-middleware设置反向代理/API网关来解决,该包与express(我们正在使用的Web服务器包)兼容。

如 Node.js 包主页上的示例所示,可以这样完成: 1. 按照 http-proxy-middleware 使用的约定定义 UNAUTH 和 AUTH API 的重定向,并在下一步中使用。

  1. 在所需端口(比如 3000)上启动一个快速 HTTPS 网络服务器,并将 http-proxy-middleware 配置为充当反向代理/API 网关。无需在 https.createServer 选项中设置 requestCert 和 rejectUnauthorized。

  2. 在另一个端口(比如 3001)上启动一个快速 HTTPS 网络服务器来服务 UNAUTH API 请求。无需在 https.createServer 选项中设置 requestCert 和 rejectUnauthorized。

  3. 在另一个端口(比如 3002)上启动一个快速 HTTPS 网络服务器来服务 AUTH API 请求。将 requestCert:true 和 rejectUnauthorized:true 设置为 https.createServer 选项。

UNAUTH 请求将从 3000 到 3001 来回路由,AUTH 请求将从 3000 到 3002 来回路由。

【讨论】:

    【解决方案3】:

    您可以通过 TLS 重新协商实现此目的,但遗憾的是 TLS 1.3 不支持此功能,因此您必须禁用它:

    https
        .createServer(
            {
                // configure key, cert, ca,
                secureOptions: require('constants').SSL_OP_NO_TLSv1_3,
            },
            function (req, res) {
                if (req.url.startsWith('/secure')) {
                    req.socket.renegotiate(
                        {
                            requestCert: true,
                            rejectUnauthorized: true,
                        },
                        err => {
                            if (err) {
                                console.log('Renegotiation failed', err);
                                res.writeHead(500);
                                res.end('Internal Server Error');
    
                                return;
                            }
    
                            // handle secure content
                        },
                    );
                } else {
                    // handle main content
                }
            },
        )
        .listen(443);
    

    【讨论】:

      【解决方案4】:

      我不确定我是否完全理解你想要什么。但根据我的理解,如果你使用 express,你可以通过一个简单的中间件来管理这个东西。在那个中间件中,你可以跟踪任何一个请求通过这个来自http或https

      HTTPS 连接具有 req.connection.encrypted(包含有关 SSL 连接信息的对象)。 HTTP 连接没有 req.connection.encrypted。

      另外(来自文档):

      使用 HTTPS 支持,使用 request.connection.verifyPeer() 和 request.connection.getPeerCertificate() 获取客户端的身份验证详细信息。

      我希望这会有所帮助..

      【讨论】:

      • 我在我的应用程序中使用 Express 框架。所有连接都已经是 https。我想要的是某些路径要求客户端使用证书进行身份验证,而其他路径则不需要。我正在使用节点 0.8.18,它似乎没有连接 verifyPeer 方法。有 getPeerCertificate 方法,它按预期工作(但它不强制客户端证书身份验证)。
      猜你喜欢
      • 1970-01-01
      • 2014-03-11
      • 1970-01-01
      • 2011-04-09
      • 2013-10-07
      • 1970-01-01
      • 2022-06-23
      • 2018-02-12
      • 2012-09-01
      相关资源
      最近更新 更多