使用 nodejs https 和 express [相互身份验证] 时,'req.socket.authorized'



我正在尝试设置具有相互身份验证的https服务器。
我为服务器创建了密钥和证书(自动签名)。

现在我使用 firefox 连接到服务器,而无需提供任何客户端证书
这应该会导致req.socket.authorizedfalse(如此处所述),但由于某种原因,经过一些刷新(并且不更改任何内容),消息从正确的
Unauthorized: Client certificate required (UNABLE_TO_GET_ISSUER_CERT)

更改为Client certificate was authenticated but certificate information could not be retrieved.

对我来说,这是出乎意料的,因为这意味着即使没有客户端证书,req.socket.authorized == true。有人可以解释我为什么会这样吗?


这是我的代码:

const express = require('express')
const app = express()
const fs = require('fs')
const https = require('https')
// ...
const opts = { key: fs.readFileSync('./cryptoMaterial/private_key.pem'),
cert: fs.readFileSync('./cryptoMaterial/certificate.pem'),
requestCert: true,
rejectUnauthorized: false,
ca: [ fs.readFileSync('./cryptoMaterial/certificate.pem') ]
}
const clientAuthMiddleware = () => (req, res, next) => {
if (!req.secure && req.header('x-forwarded-proto') != 'https') {
return res.redirect('https://' + req.header('host') + req.url);
}
// Ensure that the certificate was validated at the protocol level
if (!req.socket.authorized) { // <-- THIS SHOULD BE ALWAYS FALSE
res.status(401).send(
'Unauthorized: Client certificate required ' + 
'(' + req.socket.authorizationError + ')'
);
return
}
// Obtain certificate details
var cert = req.socket.getPeerCertificate();
if (!cert || !Object.keys(cert).length) {
// Handle the bizarre and probably not-real case that a certificate was
// validated but we can't actually inspect it
res.status(500).send(
'Client certificate was authenticated but certificate ' +
'information could not be retrieved.'
);
return
}
return next();
};
app.use(clientAuthMiddleware());
// ...
https.createServer(opts, app).listen(PORT)

我不久前遇到了同样的问题,并在 github 上创建了一个问题。这似乎是故意的行为。见 https://github.com/nodejs/node/issues/35317

引用bnoordhuis"我猜这可能是由于某些 TLS 连接重用逻辑">的回答

: 关闭,重

用的不是连接,而是TLS会话。

重用缩短了握手时间(并切断了客户端证书交换),因为它重用了以前建立的会话参数。这是每个规格,通常是您想要的。当您重新加载时,Chromium 可能会创建一个新会话。

[...]

握手期间发生验证错误(例如无效或不受信任的证书)时falsesocket.authorized,但true其他情况。

从恢复的会话开始的新连接不会执行该验证,因此假定socket.authorized = true。TLS 会话的性质如此之大,即使我们愿意,我也不确定是否可以解决此问题。


作为一种解决方法,您应该禁用 TLS 重新协商并为每个连接强制一个新的 TLS 会话,据我所知,这只能在 TLSv1.2 上完成。

以下是我如何使用 Typescript 实现它的示例:

import fs from 'fs';
import path from 'path';
import https from 'https';
import tls from 'tls';
import express from 'express';
const expressApp = express();
if (tls.DEFAULT_MAX_VERSION !== "TLSv1.2") {
throw Error('Specify --tls-max-v1.2 as a node option (see https://github.com/nodejs/node/issues/35317)');
}
const httpsOptions = {
key: fs.readFileSync(path.join('certs', 'key')),
cert: fs.readFileSync(path.join('certs', 'cert')),
ca: fs.readFileSync(path.join('certs', 'ca')),
// crl: fs.readFileSync(path.join('certs', 'crl')), /* Enable this if you have a CRL */
requestCert: true,
rejectUnauthorized: false
};
https.createServer(httpsOptions, expressApp);
/* Authentication middleware */
expressApp.use((req, res, next) => {
let tlsSocket = (req.socket as tls.TLSSocket);
if (tlsSocket.isSessionReused()) {
/* Force renegotiation (see https://github.com/nodejs/node/issues/35317) */
tlsSocket.renegotiate({ rejectUnauthorized: false, requestCert: true }, (err) => {
if (!(tlsSocket as tls.TLSSocket).authorized) {
console.log('Unauthorized');
return res.status(401).send('Unauthorized');
}
});
}
else {
if (!(tlsSocket as tls.TLSSocket).authorized) {
console.log('Unauthorized');
return res.status(401).send('Unauthorized');
}
}
next();
});

最新更新