nodejs / nodejs/node

TLSSocket does not gracefully handle peer protocol violation during TLS teardown

未关闭
#61,714 4 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

tls
主要语言
JavaScript
星标
122k
派生
37.4k
平均合并
4 天 3 小时
30 天内合并 PR
272

描述

Version

24.13.0

Platform
Have tried on these:
> Linux [hostname] 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
> Linux [hostname] 6.12.64-87.122.amzn2023.aarch64 #1 SMP Mon Jan 12 22:17:48 UTC 2026 aarch64 aarch64 aarch64 GNU/Linux
Subsystem

tls

What steps will reproduce the bug?

This is reproducible against mail.smtp2go.com on port 465.

Save the following as test.js and run with node test.js. After socket.end() is called, the process terminates due to an unhandled 'error' event emitted by TLSSocket during TLS teardown.

const tls = require('tls');

const socket = tls.connect(465, 'mail.smtp2go.com', () => {
  console.log('TLS connected');

  socket.on('data', d => console.log('DATA:', d.toString()));
  socket.on('end', () => console.log('end'));
  socket.on('finish', () => console.log('finish'));

  setTimeout(() => {
    socket.end();
    console.log('socket.end() called');
  }, 3000);
});

setInterval(() => console.log('alive'), 1000);
How often does it reproduce? Is there a required condition?

Every time, when the peer sends invalid (non‑TLS) after (or during?) TLS teardown.

What is the expected behavior? Why is that the expected behavior?

After tls.connect() completes and socket.end() is called, I expected the process to continue running. Instead, the process exited with an "Unhandled 'error' event".

I am not certain whether this behavior is intentional, especially in light of the recent Node 24.13.0 security release, or an edge case in the TLS socket lifecycle.

What do you see instead?
$ node test.js
TLS connected
DATA: 220 mail.smtp2go.com ESMTP Exim 4.99.1-S2G Sat, 07 Feb 2026 05:41:32 +0000

alive
alive
alive
socket.end() called
finish
node:events:486
      throw er; // Unhandled 'error' event
      ^

Error: C0686C2E10700000:error:0A0001BB:SSL routines:tls13_validate_record_header:bad record type:../deps/openssl/openssl/ssl/record/methods/tls13_meth.c:258:

Emitted 'error' event on TLSSocket instance at:
    at TLSSocket._emitTLSError (node:_tls_wrap:1050:10)
    at TLSWrap.onerror (node:_tls_wrap:491:11) {
  library: 'SSL routines',
  reason: 'bad record type',
  code: 'ERR_SSL_BAD_RECORD_TYPE'
}

Node.js v24.13.0
Additional information

To better understand the peer behavior involved, I verified the server interaction using OpenSSL.

Running the command openssl s_client -connect mail.smtp2go.com:465 -quiet followed by "QUIT" produced:

...
220 mail.smtp2go.com ESMTP Exim 4.99.1-S2G Sat, 07 Feb 2026 04:38:41 +0000
QUIT
221 mail.smtp2go.com closing connection

I also tested against a different SMTP server on port 465, which did not exhibit this behavior and it did not trigger the unhandled 'error' event in Node.

For additional context, I captured debug output using:
NODE_DEBUG=tls,net,stream node test.js

output
$ NODE_DEBUG=tls,net,stream node test.js
TLS 252547: client _init handle? true
NET 252547: pipe false undefined
NET 252547: connect: find host mail.smtp2go.com
NET 252547: connect: dns options { family: undefined, hints: 32 }
NET 252547: connect: autodetecting
TLS 252547: client initRead handle? true buffered? false
STREAM 252547: read 0
STREAM 252547: need readable false
STREAM 252547: length less than watermark true
STREAM 252547: do read
NET 252547: _read - n 65536 isConnecting? true hasHandle? true
NET 252547: _read wait for connection
NET 252547: connect/multiple: will try the following addresses [
...
]
NET 252547: connect/multiple: attempting to connect to 173.255.233.87:465 (addressType: 4)
NET 252547: connect/multiple: setting the attempt timeout to 250 ms
NET 252547: connect/multiple: connection attempt to 173.255.233.87:465 completed with status 0
NET 252547: afterConnect
TLS 252547: client _start handle? true connecting? false requestOCSP? false
NET 252547: _read - n 65536 isConnecting? false hasHandle? true
NET 252547: Socket._handle.readStart
STREAM 252547: read 0
STREAM 252547: need readable true
STREAM 252547: length less than watermark true
STREAM 252547: reading, ended or constructing false
TLS 252547: client onhandshakedone
TLS 252547: client _finishInit handle? true alpn false servername false
TLS 252547: client emit secureConnect. authorized: true
TLS connected
STREAM 252547: resume
STREAM 252547: resume true
STREAM 252547: flow
STREAM 252547: read undefined
STREAM 252547: need readable true
STREAM 252547: length less than watermark true
STREAM 252547: reading, ended or constructing false
STREAM 252547: push <Buffer ...>
DATA: 220 mail.smtp2go.com ESMTP Exim 4.99.1-S2G Sat, 07 Feb 2026 05:54:18 +0000

STREAM 252547: maybeReadMore read 0
STREAM 252547: read 0
STREAM 252547: need readable true
STREAM 252547: length less than watermark true
STREAM 252547: do read
NET 252547: _read - n 65536 isConnecting? false hasHandle? true
alive
alive
alive
NET 252547: _final: not ended, call shutdown()
socket.end() called
NET 252547: afterShutdown destroyed=false
finish
TLS 252547: client onerror [Error: C068302354700000:error:0A0001BB:SSL routines:tls13_validate_record_header:bad record type:../deps/openssl/openssl/ssl/record/methods/tls13_meth.c:258:
] {
  library: 'SSL routines',
  reason: 'bad record type',
  code: 'ERR_SSL_BAD_RECORD_TYPE'
} had? false
node:events:486
      throw er; // Unhandled 'error' event
      ^

Error: C068302354700000:error:0A0001BB:SSL routines:tls13_validate_record_header:bad record type:../deps/openssl/openssl/ssl/record/methods/tls13_meth.c:258:

Emitted 'error' event on TLSSocket instance at:
    at TLSSocket._emitTLSError (node:_tls_wrap:1050:10)
    at TLSWrap.onerror (node:_tls_wrap:491:11) {
  library: 'SSL routines',
  reason: 'bad record type',
  code: 'ERR_SSL_BAD_RECORD_TYPE'
}

Node.js v24.13.0

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从提供的 test.js 复现程序和 TLS teardown 跟踪开始,然后检查堆栈和调试输出中显示的 node:_tls_wrap 位置。确定 socket.end() 之后如何处理对端的无效记录,并使用 mail.smtp2go.com 复现验证行为。完成标准是 teardown 不再导致未处理的 TLSSocket 错误,同时报告的场景仍由回归测试覆盖。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, nodejs
领域
networking, security
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
50/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。