Express.js HTTPS API在AWS Lightsail运行数小时后出现SSL类错误求助
Node.js HTTPS API SSL相关错误排查与解决
我用Node.js开发了网站联系表单的API,部署在AWS Lightsail上,通过Cloudflare获取免费SSL证书。API启动5-6小时后,控制台会出现不影响运行的错误,初始警告如下:
(node:5532) Warning: An error event has already been emitted on the socket. Please use the destroy method on the socket while handling a 'clientError' event. at warnUnclosedSocket (node:_http_server:839:11) at TLSSocket.socketOnError (node:_http_server:853:5) at onParserExecuteCommon (node:_http_server:888:19) at onParserExecute (node:_http_server:809:3)
更新内容
我在代码中添加了打印错误到控制台的逻辑:
server.on("clientError", (error, socket) => { console.log(error) });
得到了一系列不同的错误:
[Error: A00D0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:c:\ws\deps\openssl\openssl\ssl\record\ssl3_record.c:355: ] { library: 'SSL routines', reason: 'wrong version number', code: 'ERR_SSL_WRONG_VERSION_NUMBER' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } [Error: A00D0000:error:0A0000C1:SSL routines:tls_post_process_client_hello:no shared cipher:c:\ws\deps\openssl\openssl\ssl\statem\statem_srvr.c:2241: ] { library: 'SSL routines', reason: 'no shared cipher', code: 'ERR_SSL_NO_SHARED_CIPHER' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } [Error: A00D0000:error:0A000102:SSL routines:tls_early_post_process_client_hello:unsupported protocol:c:\ws\deps\openssl\openssl\ssl\statem\statem_srvr.c:1677: ] { library: 'SSL routines', reason: 'unsupported protocol', code: 'ERR_SSL_UNSUPPORTED_PROTOCOL' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Error: read ECONNRESET at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } [Error: A00D0000:error:0A0000C1:SSL routines:tls_early_post_process_client_hello:no shared cipher:c:\ws\deps\openssl\openssl\ssl\statem\statem_srvr.c:1762: ] { library: 'SSL routines', reason: 'no shared cipher', code: 'ERR_SSL_NO_SHARED_CIPHER' } [Error: A00D0000:error:0A00006C:SSL routines:tls_parse_ctos_key_share:bad key share:c:\ws\deps\openssl\openssl\ssl\statem\extensions_srvr.c:646: ] { library: 'SSL routines', reason: 'bad key share', code: 'ERR_SSL_BAD_KEY_SHARE' }
我的server.js代码:
const express = require('express'); const chalk = require("chalk"); const https = require('https'); const fs = require('fs'); const app = express(); const options = { key: fs.readFileSync('./ssl/private-key.pem'), cert: fs.readFileSync('./ssl/certificate.pem'), }; const server = https.createServer(options, app); server.listen(443, () => { console.log(chalk.magenta(`[API]: `) + chalk.green('PORT : [ 443 ]')) });
package.json代码:
{ "name": "contact-api", "version": "1.0.0", "description": "Contact API", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "MIT", "dependencies": { "axios": "^1.4.0", "body-parser": "^1.20.2", "chalk": "^4.1.2", "cors": "^2.8.5", "crypto": "^1.0.1", "deep-email-validator": "^0.1.21", "express": "^4.18.2", "express-rate-limit": "^6.7.1", "figlet": "^1.6.0", "fs": "^0.0.1-security", "https": "^1.0.0", "moment": "^2.29.4", "nodemailer": "^6.9.3" } }
问题原因与解决方案
原因分析
这些错误大多来自恶意扫描或不兼容的客户端请求:
ERR_SSL_WRONG_VERSION_NUMBER/ERR_SSL_UNSUPPORTED_PROTOCOL:客户端使用了过时的SSL/TLS版本(如SSLv3、TLSv1.0)连接服务,Node.js默认不支持这类不安全协议。ERR_SSL_NO_SHARED_CIPHER/ERR_SSL_BAD_KEY_SHARE:客户端加密套件与服务不匹配,或是恶意构造的握手请求。ECONNRESET:客户端在连接建立过程中主动断开,可能是扫描工具中断请求,或是网络波动导致。
初始警告是因为处理clientError事件时未正确销毁socket,触发Node.js的警告机制。
解决方案
- 正确处理clientError事件,消除警告
修改clientError处理逻辑,按Node.js提示调用socket.destroy(),同时可选择性过滤频繁的非关键错误,避免日志刷屏:
server.on("clientError", (error, socket) => { // 仅记录非ECONNRESET的关键错误 if (!error.message.includes('ECONNRESET')) { console.error('Client error:', error); } // 必须调用destroy关闭socket socket.destroy(); });
- 限制SSL/TLS版本与加密套件,提升安全性
在HTTPS服务器选项中明确指定支持的安全TLS版本和加密套件,拒绝过时协议:
const options = { key: fs.readFileSync('./ssl/private-key.pem'), cert: fs.readFileSync('./ssl/certificate.pem'), // 仅支持安全的TLS版本 minVersion: 'TLSv1.2', maxVersion: 'TLSv1.3', // 指定现代安全加密套件(可按需调整) ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384' };
- 利用Cloudflare过滤恶意请求
既然已使用Cloudflare,直接在后台开启安全功能拦截无效请求:
- 启用SSL/TLS严格模式,让Cloudflare处理前端SSL握手,后端只需与Cloudflare通信(可改为HTTP服务,简化本地SSL配置)。
- 将安全级别设为“本质安全”或更高,自动拦截恶意扫描和过时协议请求。
- 开启Bot Fight Mode或Super Bot Fight Mode,拦截自动化扫描工具。
- 可选:改用Cloudflare Tunnel替代本地SSL配置
若不想在Lightsail上维护SSL证书,可使用Cloudflare Tunnel将本地服务直接暴露到Cloudflare网络,所有SSL由Cloudflare处理,既简化配置又提升安全性。
内容的提问来源于stack exchange,提问作者Enes Emre
相关产品推荐
相关产品推荐

