fix(TLS/SNI): Improve TLS session resumption handling and logging. Now, session resumption attempts are always logged with details, and connections without a proper SNI are rejected when allowSessionTicket is disabled. In addition, empty SNI extensions are explicitly treated as missing, ensuring stricter and more consistent TLS handshake validation.
This commit is contained in:
parent
b48b90d613
commit
1a90566622
@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-03-12 - 3.41.3 - fix(TLS/SNI)
|
||||||
|
Improve TLS session resumption handling and logging. Now, session resumption attempts are always logged with details, and connections without a proper SNI are rejected when allowSessionTicket is disabled. In addition, empty SNI extensions are explicitly treated as missing, ensuring stricter and more consistent TLS handshake validation.
|
||||||
|
|
||||||
|
- Always log session resumption in both renegotiation and initial ClientHello processing.
|
||||||
|
- Terminate connections that attempt session resumption without SNI when allowSessionTicket is false.
|
||||||
|
- Treat empty SNI extensions as absence of SNI to improve consistency in TLS handshake processing.
|
||||||
|
|
||||||
## 2025-03-11 - 3.41.2 - fix(SniHandler)
|
## 2025-03-11 - 3.41.2 - fix(SniHandler)
|
||||||
Refactor hasSessionResumption to return detailed session resumption info
|
Refactor hasSessionResumption to return detailed session resumption info
|
||||||
|
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '3.41.2',
|
version: '3.41.3',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
@ -943,23 +943,31 @@ export class PortProxy {
|
|||||||
// Analyze for session resumption attempt (session ticket or PSK)
|
// Analyze for session resumption attempt (session ticket or PSK)
|
||||||
const resumptionInfo = SniHandler.hasSessionResumption(renegChunk, this.settings.enableTlsDebugLogging);
|
const resumptionInfo = SniHandler.hasSessionResumption(renegChunk, this.settings.enableTlsDebugLogging);
|
||||||
|
|
||||||
// Only block if there's a session ticket without SNI
|
if (resumptionInfo.isResumption) {
|
||||||
if (resumptionInfo.isResumption && !resumptionInfo.hasSNI) {
|
// Always log resumption attempt for easier debugging
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket detected in renegotiation without SNI and allowSessionTicket=false. ` +
|
`[${connectionId}] Session resumption detected in renegotiation. ` +
|
||||||
|
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Block if there's session resumption without SNI
|
||||||
|
if (!resumptionInfo.hasSNI) {
|
||||||
|
console.log(
|
||||||
|
`[${connectionId}] Session resumption detected in renegotiation without SNI and allowSessionTicket=false. ` +
|
||||||
`Terminating connection to force new TLS handshake.`
|
`Terminating connection to force new TLS handshake.`
|
||||||
);
|
);
|
||||||
this.initiateCleanupOnce(record, 'session_ticket_blocked');
|
this.initiateCleanupOnce(record, 'session_ticket_blocked');
|
||||||
return;
|
return;
|
||||||
} else if (resumptionInfo.isResumption && resumptionInfo.hasSNI) {
|
} else {
|
||||||
if (this.settings.enableTlsDebugLogging) {
|
if (this.settings.enableDetailedLogging) {
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket with SNI detected in renegotiation. ` +
|
`[${connectionId}] Session resumption with SNI detected in renegotiation. ` +
|
||||||
`Allowing connection since SNI is present.`
|
`Allowing connection since SNI is present.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const newSNI = SniHandler.extractSNIWithResumptionSupport(renegChunk, connInfo, this.settings.enableTlsDebugLogging);
|
const newSNI = SniHandler.extractSNIWithResumptionSupport(renegChunk, connInfo, this.settings.enableTlsDebugLogging);
|
||||||
|
|
||||||
@ -1575,10 +1583,17 @@ export class PortProxy {
|
|||||||
// Analyze for session resumption attempt
|
// Analyze for session resumption attempt
|
||||||
const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
|
const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
|
||||||
|
|
||||||
// Only block if there's a session ticket without SNI
|
if (resumptionInfo.isResumption) {
|
||||||
if (resumptionInfo.isResumption && !resumptionInfo.hasSNI) {
|
// Always log resumption attempt for easier debugging
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket detected in initial ClientHello without SNI and allowSessionTicket=false. ` +
|
`[${connectionId}] Session resumption detected in initial ClientHello. ` +
|
||||||
|
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Block if there's session resumption without SNI
|
||||||
|
if (!resumptionInfo.hasSNI) {
|
||||||
|
console.log(
|
||||||
|
`[${connectionId}] Session resumption detected in initial ClientHello without SNI and allowSessionTicket=false. ` +
|
||||||
`Terminating connection to force new TLS handshake.`
|
`Terminating connection to force new TLS handshake.`
|
||||||
);
|
);
|
||||||
if (connectionRecord.incomingTerminationReason === null) {
|
if (connectionRecord.incomingTerminationReason === null) {
|
||||||
@ -1588,15 +1603,16 @@ export class PortProxy {
|
|||||||
socket.end();
|
socket.end();
|
||||||
this.cleanupConnection(connectionRecord, 'session_ticket_blocked');
|
this.cleanupConnection(connectionRecord, 'session_ticket_blocked');
|
||||||
return;
|
return;
|
||||||
} else if (resumptionInfo.isResumption && resumptionInfo.hasSNI) {
|
} else {
|
||||||
if (this.settings.enableTlsDebugLogging) {
|
if (this.settings.enableDetailedLogging) {
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket with SNI detected in initial ClientHello. ` +
|
`[${connectionId}] Session resumption with SNI detected in initial ClientHello. ` +
|
||||||
`Allowing connection since SNI is present.`
|
`Allowing connection since SNI is present.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to extract SNI for domain-specific NetworkProxy handling
|
// Try to extract SNI for domain-specific NetworkProxy handling
|
||||||
const connInfo = {
|
const connInfo = {
|
||||||
@ -1949,10 +1965,17 @@ export class PortProxy {
|
|||||||
// Analyze for session resumption attempt
|
// Analyze for session resumption attempt
|
||||||
const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
|
const resumptionInfo = SniHandler.hasSessionResumption(chunk, this.settings.enableTlsDebugLogging);
|
||||||
|
|
||||||
// Only block if there's a session ticket without SNI
|
if (resumptionInfo.isResumption) {
|
||||||
if (resumptionInfo.isResumption && !resumptionInfo.hasSNI) {
|
// Always log resumption attempt for easier debugging
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket detected in initial ClientHello without SNI and allowSessionTicket=false. ` +
|
`[${connectionId}] Session resumption detected in SNI handler. ` +
|
||||||
|
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Block if there's session resumption without SNI
|
||||||
|
if (!resumptionInfo.hasSNI) {
|
||||||
|
console.log(
|
||||||
|
`[${connectionId}] Session resumption detected in SNI handler without SNI and allowSessionTicket=false. ` +
|
||||||
`Terminating connection to force new TLS handshake.`
|
`Terminating connection to force new TLS handshake.`
|
||||||
);
|
);
|
||||||
if (connectionRecord.incomingTerminationReason === null) {
|
if (connectionRecord.incomingTerminationReason === null) {
|
||||||
@ -1962,15 +1985,16 @@ export class PortProxy {
|
|||||||
socket.end();
|
socket.end();
|
||||||
this.cleanupConnection(connectionRecord, 'session_ticket_blocked');
|
this.cleanupConnection(connectionRecord, 'session_ticket_blocked');
|
||||||
return;
|
return;
|
||||||
} else if (resumptionInfo.isResumption && resumptionInfo.hasSNI) {
|
} else {
|
||||||
if (this.settings.enableTlsDebugLogging) {
|
if (this.settings.enableDetailedLogging) {
|
||||||
console.log(
|
console.log(
|
||||||
`[${connectionId}] Session ticket with SNI detected in initial ClientHello. ` +
|
`[${connectionId}] Session resumption with SNI detected in SNI handler. ` +
|
||||||
`Allowing connection since SNI is present.`
|
`Allowing connection since SNI is present.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create connection info object for SNI extraction
|
// Create connection info object for SNI extraction
|
||||||
const connInfo = {
|
const connInfo = {
|
||||||
|
@ -410,8 +410,13 @@ export class SniHandler {
|
|||||||
pos += 2;
|
pos += 2;
|
||||||
|
|
||||||
if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
|
if (extensionType === this.TLS_SNI_EXTENSION_TYPE) {
|
||||||
|
// Check that the SNI extension actually has content
|
||||||
|
if (extensionLength > 0) {
|
||||||
hasSNI = true;
|
hasSNI = true;
|
||||||
log('Found SNI extension');
|
log('Found SNI extension with length: ' + extensionLength);
|
||||||
|
} else {
|
||||||
|
log('Found empty SNI extension, treating as no SNI');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +443,15 @@ export class SniHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return an object with both flags
|
// Return an object with both flags
|
||||||
|
// For clarity: connections should be blocked if they have session resumption without SNI
|
||||||
|
if (isResumption) {
|
||||||
|
log(`Resumption summary - hasSNI: ${hasSNI ? 'yes' : 'no'}, resumption type: ${
|
||||||
|
hasSessionTicket ? 'session ticket, ' : ''
|
||||||
|
}${hasPSK ? 'PSK, ' : ''}${hasEarlyData ? 'early data, ' : ''}${
|
||||||
|
hasNonEmptySessionId ? 'session ID' : ''
|
||||||
|
}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isResumption,
|
isResumption,
|
||||||
hasSNI
|
hasSNI
|
||||||
|
Loading…
x
Reference in New Issue
Block a user