fix(connectionhandler): Send proper TLS alert before terminating connections when SNI is missing and session tickets are disallowed.
This commit is contained in:
parent
fe60f88746
commit
97982976c8
@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-03-15 - 4.1.2 - fix(connectionhandler)
|
||||||
|
Send proper TLS alert before terminating connections when SNI is missing and session tickets are disallowed.
|
||||||
|
|
||||||
|
- Added logic to transmit a fatal TLS alert (Handshake Failure) before closing the connection when no SNI is present with allowSessionTicket=false.
|
||||||
|
- Introduced a slight 50ms delay after sending the alert to ensure the client receives the alert properly.
|
||||||
|
- Applied these changes both for the initial ClientHello and when handling subsequent TLS data.
|
||||||
|
|
||||||
## 2025-03-15 - 4.1.1 - fix(tls)
|
## 2025-03-15 - 4.1.1 - fix(tls)
|
||||||
Enforce strict SNI handling in TLS connections by terminating ClientHello messages lacking SNI when session tickets are disallowed and removing legacy session cache code.
|
Enforce strict SNI handling in TLS connections by terminating ClientHello messages lacking SNI when session tickets are disallowed and removing legacy session cache code.
|
||||||
|
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '4.1.1',
|
version: '4.1.2',
|
||||||
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
||||||
}
|
}
|
||||||
|
@ -174,16 +174,40 @@ export class ConnectionHandler {
|
|||||||
|
|
||||||
// If allowSessionTicket is false and we can't determine SNI, terminate the connection
|
// If allowSessionTicket is false and we can't determine SNI, terminate the connection
|
||||||
if (!serverName) {
|
if (!serverName) {
|
||||||
|
// Always block when allowSessionTicket is false and there's no SNI
|
||||||
|
// Don't even check for session resumption - be strict
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] No SNI detected in ClientHello and allowSessionTicket=false. ` +
|
`[${connectionId}] No SNI detected in ClientHello and allowSessionTicket=false. ` +
|
||||||
`Terminating connection to force new TLS handshake with SNI.`
|
`Terminating connection to force new TLS handshake with SNI.`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Send a proper TLS alert before ending the connection
|
||||||
|
// This helps browsers like Chrome properly recognize the error
|
||||||
|
const alertData = Buffer.from([
|
||||||
|
0x15, // Alert record type
|
||||||
|
0x03, 0x03, // TLS 1.2 version
|
||||||
|
0x00, 0x02, // Length
|
||||||
|
0x02, // Fatal alert level
|
||||||
|
0x40 // Handshake failure alert
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.write(alertData);
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore write errors, we're closing anyway
|
||||||
|
}
|
||||||
|
|
||||||
if (record.incomingTerminationReason === null) {
|
if (record.incomingTerminationReason === null) {
|
||||||
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
||||||
this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
|
this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
|
||||||
}
|
}
|
||||||
socket.end();
|
|
||||||
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
// Add a small delay before ending to allow alert to be sent
|
||||||
|
setTimeout(() => {
|
||||||
|
socket.end();
|
||||||
|
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
||||||
|
}, 50);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,32 +572,40 @@ export class ConnectionHandler {
|
|||||||
this.tlsManager.isClientHello(chunk) &&
|
this.tlsManager.isClientHello(chunk) &&
|
||||||
!serverName) {
|
!serverName) {
|
||||||
|
|
||||||
// Check if this is a session resumption
|
// Always block ClientHello without SNI when allowSessionTicket is false
|
||||||
const resumptionInfo = this.tlsManager.handleSessionResumption(
|
// Don't even check for session resumption - be strict
|
||||||
chunk,
|
console.log(
|
||||||
connectionId,
|
`[${connectionId}] No SNI detected in ClientHello and allowSessionTicket=false. ` +
|
||||||
false // No SNI
|
`Terminating connection to force new TLS handshake with SNI.`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resumptionInfo.shouldBlock) {
|
// Send a proper TLS alert before ending the connection
|
||||||
console.log(
|
const alertData = Buffer.from([
|
||||||
`[${connectionId}] Session resumption without SNI detected and allowSessionTicket=false. ` +
|
0x15, // Alert record type
|
||||||
`Terminating connection to force new TLS handshake with SNI.`
|
0x03, 0x03, // TLS 1.2 version
|
||||||
);
|
0x00, 0x02, // Length
|
||||||
if (record.incomingTerminationReason === null) {
|
0x02, // Fatal alert level
|
||||||
record.incomingTerminationReason = resumptionInfo.reason || 'session_ticket_blocked_no_sni';
|
0x40 // Handshake failure alert
|
||||||
this.connectionManager.incrementTerminationStat(
|
]);
|
||||||
'incoming',
|
|
||||||
resumptionInfo.reason || 'session_ticket_blocked_no_sni'
|
try {
|
||||||
);
|
socket.write(alertData);
|
||||||
}
|
} catch (err) {
|
||||||
socket.end();
|
// Ignore write errors, we're closing anyway
|
||||||
this.connectionManager.cleanupConnection(
|
|
||||||
record,
|
|
||||||
resumptionInfo.reason || 'session_ticket_blocked_no_sni'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (record.incomingTerminationReason === null) {
|
||||||
|
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
||||||
|
this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a small delay before ending to allow alert to be sent
|
||||||
|
setTimeout(() => {
|
||||||
|
socket.end();
|
||||||
|
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user