fix(tls/sni): Improve logging for TLS session resumption by extracting and logging SNI values from ClientHello messages.

This commit is contained in:
Philipp Kunz 2025-03-12 10:01:54 +00:00
parent 156abbf5b4
commit ab1ec84832
4 changed files with 52 additions and 5 deletions

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## 2025-03-12 - 3.41.4 - fix(tls/sni)
Improve logging for TLS session resumption by extracting and logging SNI values from ClientHello messages.
- Added logging to output the extracted SNI value during renegotiation, initial ClientHello and in the SNI handler.
- Enhanced error handling during SNI extraction to aid troubleshooting of TLS session resumption issues.
## 2025-03-12 - 3.41.3 - fix(TLS/SNI) ## 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. 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.

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '3.41.3', version: '3.41.4',
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.'
} }

View File

@ -945,9 +945,13 @@ export class PortProxy {
if (resumptionInfo.isResumption) { if (resumptionInfo.isResumption) {
// Always log resumption attempt for easier debugging // Always log resumption attempt for easier debugging
// Try to extract SNI for logging
const extractedSNI = SniHandler.extractSNI(renegChunk, this.settings.enableTlsDebugLogging);
console.log( console.log(
`[${connectionId}] Session resumption detected in renegotiation. ` + `[${connectionId}] Session resumption detected in renegotiation. ` +
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}` `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
`SNI value: ${extractedSNI || 'None'}, ` +
`allowSessionTicket: ${this.settings.allowSessionTicket}`
); );
// Block if there's session resumption without SNI // Block if there's session resumption without SNI
@ -1585,9 +1589,13 @@ export class PortProxy {
if (resumptionInfo.isResumption) { if (resumptionInfo.isResumption) {
// Always log resumption attempt for easier debugging // Always log resumption attempt for easier debugging
// Try to extract SNI for logging
const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
console.log( console.log(
`[${connectionId}] Session resumption detected in initial ClientHello. ` + `[${connectionId}] Session resumption detected in initial ClientHello. ` +
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}` `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
`SNI value: ${extractedSNI || 'None'}, ` +
`allowSessionTicket: ${this.settings.allowSessionTicket}`
); );
// Block if there's session resumption without SNI // Block if there's session resumption without SNI
@ -1967,9 +1975,13 @@ export class PortProxy {
if (resumptionInfo.isResumption) { if (resumptionInfo.isResumption) {
// Always log resumption attempt for easier debugging // Always log resumption attempt for easier debugging
// Try to extract SNI for logging
const extractedSNI = SniHandler.extractSNI(chunk, this.settings.enableTlsDebugLogging);
console.log( console.log(
`[${connectionId}] Session resumption detected in SNI handler. ` + `[${connectionId}] Session resumption detected in SNI handler. ` +
`Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, allowSessionTicket: ${this.settings.allowSessionTicket}` `Has SNI: ${resumptionInfo.hasSNI ? 'Yes' : 'No'}, ` +
`SNI value: ${extractedSNI || 'None'}, ` +
`allowSessionTicket: ${this.settings.allowSessionTicket}`
); );
// Block if there's session resumption without SNI // Block if there's session resumption without SNI

View File

@ -413,7 +413,36 @@ export class SniHandler {
// Check that the SNI extension actually has content // Check that the SNI extension actually has content
if (extensionLength > 0) { if (extensionLength > 0) {
hasSNI = true; hasSNI = true;
log('Found SNI extension with length: ' + extensionLength);
// Try to extract the actual SNI value for logging
try {
// Skip to server_name_list_length (2 bytes)
const tempPos = pos;
if (tempPos + 2 <= extensionsEnd) {
const nameListLength = (buffer[tempPos] << 8) + buffer[tempPos + 1];
// Skip server_name_list_length (2 bytes)
if (tempPos + 2 + 1 <= extensionsEnd) {
// Check name_type (should be 0 for hostname)
if (buffer[tempPos + 2] === 0) {
// Skip name_type (1 byte)
if (tempPos + 3 + 2 <= extensionsEnd) {
// Get name_length (2 bytes)
const nameLength = (buffer[tempPos + 3] << 8) + buffer[tempPos + 4];
// Extract the hostname
if (tempPos + 5 + nameLength <= extensionsEnd) {
const hostname = buffer.slice(tempPos + 5, tempPos + 5 + nameLength).toString('utf8');
log(`Found SNI extension with server_name: ${hostname}`);
}
}
}
}
}
} catch (e) {
log(`Error extracting SNI value: ${e}`);
log('Found SNI extension with length: ' + extensionLength);
}
} else { } else {
log('Found empty SNI extension, treating as no SNI'); log('Found empty SNI extension, treating as no SNI');
} }