-
Notifications
You must be signed in to change notification settings - Fork 30.9k
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
clientError emitted for wrong socket - SSL alert number 48 #1642
Comments
I have confirmed and reproduced this. But it is nothing do with iojs. For example, I can also reproduce this with an diff --git a/index.html b/index.html
index f2f081a..db427a2 100644
--- a/index.html
+++ b/index.html
@@ -9,7 +9,7 @@
<script type="text/javascript">
function makeRequest() {
var req = new XMLHttpRequest();
- req.open('GET', location.href, true);
+ req.open('GET', location.href + '/index.html', true);
req.onload = function (ev) {
setTimeout(makeRequest, 10) $ openssl s_server -key agent.key -cert agent.crt -accept 4444 -WWW -msg >& server.log 2>&1 Following the above steps with changing url to index.html, the sameTLS Alert is found in the server.log.
From my analysis of the packet dump, the ajax in Chrome seems to steal the first TLS connection of Firefox and send the request to server on the Firefox connection but it is not accepted with permission yet then the client sends TLS alert of unknown_ca and the sever close the connection. It is very strange behavior and I'm not sure its reason. I think this issue comes from the client behavior. I'm closing this issue for now. If you find any clue that this is caused by iojs, please be free to put the comment here. I will reopen this. |
Thank you for your prompt response. I have a few questions:
Thanks. |
I don't think we can blame the client so easily here. Something seemed really weird to me, so i did a couple of tests....
var https = require('https');
var pem = require('pem');
var fs = require('fs');
var indexHtml = fs.readFileSync('index.html');
pem.createCertificate({ commonName:'10.10.5.75:4444', days: 10, selfSigned: true }, function (err, keys) {
if (err) throw err;
var httpServer = https.createServer({
key: keys.serviceKey,
cert: keys.certificate
}, function (req, res) {
req.socket.userAgent = req.headers['user-agent'];
req.socket.c_remoteAddress = req.socket.remoteAddress;
req.socket.c_remotePort = req.socket.remotePort;
res.writeHead('200', {
'Content-Type': 'text/html',
'Content-Length': indexHtml.length
});
res.end(indexHtml);
});
httpServer.on('clientError', function (err, socket) {
console.log(err.stack);
console.log('Triggered on client with UA: ' + socket.userAgent);
console.log('From: ', socket.c_remoteAddress, socket.c_remotePort);
//console.log('Socket: ', socket);
});
httpServer.listen(4444,'10.10.5.75');
}); With https://10.10.5.75:4444/ loaded in Chrome on W7Chrome vm, when i typed https://10.10.5.75:4444/ in Firefox location bar on W7FF vm, the server script spitted the following:
Well i have 0 knowledge of tls protocol or its flows; but when Firefox makes that initial https request as i open the url, an error appears matched/paired with a socket that has different client port and client ip (for a different pc/vm). It really is weird that it could happen in the same machine, but I seems really unlikely that one process in one machine is stealing a socket (l_ip,l_port,r_ip,r_port tuple) from another process... in another machine! TCP Hijacking is possible, but difficult to perform and low success rate, yet it would be happening spontaneously and consistently... no way. Also tested with bridged adapter network and some non-virtual client machines, same results. I have also tried:
...opening the url with a Firefox client does consistently give something like:
...that is, it seems to always send fatal unknown_ca (48, 0x30) right after the tls handshake is finished when connecting to server with cert. which has no trusted ca (and no exception has been added previously in the browser). I don't really know, but i have a feeling that it could be an OpenSSL bug (iojs is not without lottery tickets, but i think the last versions of openssl have bought plenty more numbers). My bet, in short: some openssl internal state related to chrome connections makes it handle the alert msg incorrectly and bind it with one of those chrome connections??? Added after @calzoneman 2nd post: PD: Sorry for the long post, and as always, thanx. EDIT: Let's just hope it isn't another critical vulnerability in openssl... (like a dos-enabling vuln.). |
~~@calzoneman:
httpServer.on('clientError', function (err, socket) { //EDIT: USELESS
var ignoreThisError=false;
if(/:SSL alert number 48\D/.test(err.message)) ignoreThisError=true;
//....
}
EDIT: It's impossible to skip closing the socket in user-land: it's not being closed in user-land, but by iojs. No user-land workaround. |
Reopen this because I've just found that this is the issue of iojs. I should have taken care of the UA property defined in the request event at the above test. Because the fatal TLS alert was sent from client to server immediately after SSL handshake completed, there was no way to carry encrypted payload in the connection so that the request event never happen on https.Server. The UA propety of socket had no chance to be defined and should be
iojs might incorrectly handle the ssl error on the wrong socket and it destroy the existing connection from Chrome. @calzoneman @gopat Could you please test my branch of @indutny Do you have any comments on my fixes in the above branch? The fix of |
Wow @shigeki , that's just great! Tested it with: var https = require('https');
var pem = require('pem');
var fs = require('fs');
var indexHtml = fs.readFileSync('index.html');
pem.createCertificate({ commonName:'10.10.5.75:4444', days: 10, selfSigned: true }, function (err, keys) {
if (err) throw err;
var httpServer = https.createServer({
key: keys.serviceKey,
cert: keys.certificate
}, function (req, res) {
req.socket.userAgent = req.headers['user-agent'];
res.writeHead('200', {
'Content-Type': 'text/html',
'Content-Length': indexHtml.length
});
res.end(indexHtml);
});
httpServer.on('connection', function (socket) { //get ip+port before tls does anything
socket.c_remoteAddress = socket.remoteAddress;
socket.c_remotePort = socket.remotePort;
})
httpServer.on('clientError', function (err, socket) {
var ignoreThisError=false;
if(/:SSL alert number 48\D/.test(err.message)) ignoreThisError=true;
console.log(err.message);
console.log('Triggered on client with UA: ' + socket.userAgent);
//hacky way to get ip+port saved in 'connection' event:
console.log('From: ', socket._parent.c_remoteAddress, socket._parent.c_remotePort);
});
httpServer.listen(4444,'10.10.5.75');
}); Result:
I'd wait for @calzoneman tests also, but i think it's fixed 👌 . @shigeki :
Well, we're humans after all... |
@gopat Thanks. Without your comment, I would not have found myself be wrong. I appreciate your help very much. |
@shigeki left a comment, thank you! |
@calzoneman Thanks for testing and I'm sorry for my wrong explanation and decision at the first comment. I've just submitted a PR of #1661 to fix this. If you need, I don't mind to submit this to Joyent node-v0.12 but to be sure that it needs another reviews. |
@shigeki No problem, and thank you for fixing this! I think that we should contribute the fix to joyent/node since they haven't commented on the issue at all, but it's not urgent for me since I can use io.js or apply the fix manually. |
Fixed in e008e8f and 7c52e1c |
NOTE: The following bug report was originally submitted to the node team at nodejs/node-v0.x-archive#14818; however, I encountered the same issue in io.js when trying it out. I have been able to reproduce the issue on io.js compiled from source from the master branch (v2.0.1), both on my Arch desktop and Debian server. It's worth noting that the issue does not occur on the v0.10 branch of node.js, only the v0.12 branch.
I've noticed a peculiar behavior of the
https
module with regard to Firefox closing connections for untrusted certificates. In the demo below, I use a self-signed certificate generated by thepem
module, but the same principle can be applied to any untrusted certificate (for example, I was able to cause the same issue by deleting the Startcom signing certificates from Firefox and attempting to connect to a server with a StartSSL cert).What happens is that as soon as a Firefox client that doesn't trust the certificate terminates its connection, the
https
server fires aclientError
event with an SSL error-- except this event is fired on a random socket that has no relation to the Firefox client. I determined this by gathering several different Chrome users, having them all connect to a server I controlled, and observing the errors logged when I connect with Firefox.I would expect instead that the
socket
parameter passed to theclientError
event handler would be the client that triggered the error, not some arbitrary other client.Steps to reproduce:
Download server.js and index.html as listed below
Install the
pem
moduleStart the server, and navigate Chromium to
https://localhost:4444
Navigate Firefox to
https://localhost:4444
Observe that as soon as the Untrusted Connection page displays in Firefox, a
clientError
is triggered on the server, but on the Chromium client:Demo:
server.js
index.html
The text was updated successfully, but these errors were encountered: