-
Notifications
You must be signed in to change notification settings - Fork 284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tlsSocket.servername is false (sometimes) #4466
Comments
Can you simplify that reproduction to be smaller? Your reproduction has a lot of moving parts, it's easier if you have a one-and-done script that showcases this behavior. |
Hello RedYetiDev, i'm afraid a fully reproducible https-setup as a one-and-done script isn't that easy to provide. Nevertheless I have tried to simplify the above example as much as possible and let _https_ = require("https");
let _fs_ = require("fs");
var _tlsOptions = {
name : "foo.com",
cert : _fs_.readFileSync('test_cert.pem'),
key : _fs_.readFileSync('test_key.pem'),
ca : _fs_.readFileSync('test_ca.pem')
};
var httpsOptions = {
SNICallback: onSecureEstablish,
minVersion: 'TLSv1.2'
};
var _server = _https_.createServer(httpsOptions, onSecureConnect);
_server.on('tlsClientError',onSecureFailed);
_server.listen(443);
var onSecureEstablish = function onSecureEstablish(servername, callback) {
/* SNICallback function (simplified) */
if (servername === "foo.com") { /* servername valid */
console.log("SSL-Establish - for [ foo.com ]");
return callback(null, new _tls_.createSecureContext(_tlsOptions) );
/* -> onSecureConnect */
};
/* else - ERR */
let _err = new Error("HOST_NOT_ALLOWED");
return callback(_err,undefined);
/* -> onSecureFailed */
};
var onSecureFailed = function onSecureFailed(err,socket) {
/* tlsClienError function (simplified) */
console.log("HTTPS Connect ERR -> SSL-Establish failed : "+err);
};
var onSecureConnect = function onSecureConnect(request, response) {
// SNICallback sucsessfully done at this point
// request.socket is the tlsSocket
/* verify host & servername */
var _servername = request.socket.servername;
var _host = request.headers.host;
if (!_servername || _servername === false) { /* THIS is where the ERROR occurs ! */
console.log("HTTPS Connect ERR -> missing required SNI !");
response.writeHead(400);
response.end("ERR - 400 Bad Request");
return;
}
if (!_host) {
console.log("HTTPS Connect ERR -> missing required header [ Host ] !");
response.writeHead(400);
response.end("ERR - 400 Bad Request");
return;
};
// servername & host MUST match !!
if (_servername !== _host) {
console.log("HTTPS Connect ERR -> SNI [ "+_servername+" ] and Host [ "+_host+" ] do not match !");
response.writeHead(400);
response.end("ERR - 400 Bad Request");
return;
};
/* OK - process request -> response */
response.writeHead(200);
response.end("response from "+_host);
return;
}; |
According to : https://nodejs.org/docs/latest/api/tls.html#event-secureconnection and https://nodejs.org/docs/latest/api/tls.html#event-tlsclienterror My extensive logging showed:
but still - sometimes the tlsSocket.servername is falsesomewhere in between the I am aware of
Related:nodejs/node#27699 and |
@siggi-gross could you check if the session was reused? I think it is a know behavior if so. |
@indutny could you please point me in the right direction how to check that? This one? https://nodejs.org/docs/latest/api/tls.html#session-resumption In Thank You in advance |
@indutny - yes, session was resued... After two more days of extensive logging and investigating into the topic, As it turns out, some clients try to reuse tlsSession on IP basis (for good) or on
which effectively means - in As I strictly need the SNI servername there to proceed with the request, |
Node.js Version
v18.19.0
NPM Version
v9
Operating System
linux
Subsystem
https, tls
Description
https server serving multiple domains, SNICallback in options.
On initializing the server, no default context is given, only the SNICallback.
(see code below)
What I want to achieve is:
To decide, which content to serve to the client, the response is processed by the hostname.
Works as expected
The server is running sucessfully for a while now,
rejecting all unwanted and malformed requests (mostly from bots etc).
SNICallback Errors :
OnSecureEstablish() -> OnSecureFailed()
request.socket.servername -> false
Every now and then - the servername is false after (obviously) successfull SNICallback.
SNICallback Success:
OnSecureEstabish() -> OnSecureConnect()
As the [servername - hostname] match fails, those requests are responded with 400 - Bad Request.
I have done a lot of extensive logging and investigation,
but still could not find the reason for this happening.
Observations
While some requests are clearly malicious,
some are valid (the servername for SNICallback was fine and matching to the header hostname) .
The "false negatives" often appear after a client sent multiple valid and successfull requests in a short period of time.
The majority come from user-agents (bytespider, bingbot etc.) but not all of them
Questions
Did I miss something in SNICallback ?
Is it something with TLS 1.3 session reuse, i am not aware of ?
Once in
OnSecureConnect()
how can i find out:Minimal Reproduction
Output
Before You Submit
The text was updated successfully, but these errors were encountered: