HTTPS
History
稳定性:2 - 稳定
HTTPS 是基于 TLS/SSL 的 HTTP 协议。在 Node.js 中,这是作为一个单独的模块实现的。
Node.js 有可能在不包含 node:crypto 模块支持的情况下构建。在这种情况下,尝试从 https import 或调用 require('node:https') 将导致抛出错误。
使用 CommonJS 时,抛出的错误可以使用 try/catch 捕获:
let https; try { https = require('node:https'); } catch (err) { console.error('https support is disabled!'); }
使用词法 ESM import 关键字时,只有在尝试加载模块_之前_注册了 process.on('uncaughtException') 的处理程序(例如,使用预加载模块),才能捕获错误。
使用 ESM 时,如果代码有可能在尚未启用 crypto 支持的 Node.js 构建上运行,请考虑使用 import() 函数而不是词法 import 关键字:
let https; try { https = await import('node:https'); } catch (err) { console.error('https support is disabled!'); }
类:https.Agent
History
一个用于 HTTPS 的 Agent 对象,类似于 http.Agent。详见 https.request() 获取更多信息。
类似于 http.Agent,createConnection(options[, callback]) 方法可以被重写以自定义 TLS 连接的建立方式。
详见
agent.createConnection()了解重写此方法的详细信息,包括使用回调异步创建 socket。
new Agent(options?): void
Objecthttp.Agent(options)
相同的字段,以及指定自定义 checkServerIdentity 选项的请求不符合通过 https.Agent 复用连接或 TLS 会话的条件,除非在构造 Agent 时指定了 checkServerIdentity 选项。
事件:'keylog'
History
BufferSSLKEYLOGFILE
格式。tls.TLSSockettls.TLSSocket
实例。当由此 agent 管理的连接生成或接收密钥材料时,会发出 keylog 事件(通常在握手完成之前,但不一定)。此密钥材料可以存储用于调试,因为它允许解密捕获的 TLS 流量。每个 socket 可能会发出多次。
一个典型的用例是将接收到的行追加到一个公共文本文件中,该软件(例如 Wireshark)稍后使用该文件来解密流量:
// ... https.globalAgent.on('keylog', (line, tlsSocket) => { fs.appendFileSync('/tmp/ssl-keys.log', line, { mode: 0o600 }); });
类:https.Server
History
- 继承:
tls.Server
详见 http.Server 获取更多信息。
server.close(callback?): void
Function详见 node:http 模块中的 server.close()。
server[Symbol.asyncDispose](): void
调用 server.close() 并返回一个 promise,当服务器关闭时该 promise 会 fulfilled。
server.closeAllConnections(): void
详见 node:http 模块中的 server.closeAllConnections()。
server.closeIdleConnections(): void
详见 node:http 模块中的 server.closeIdleConnections()。
- 类型:
number默认值:60000
详见 node:http 模块中的 server.headersTimeout。
server.listen(): void
启动 HTTPS 服务器监听加密连接。
此方法等同于 net.Server 中的 server.listen()。
- 类型:
number默认值:2000
详见 node:http 模块中的 server.maxHeadersCount。
- 类型:
number默认值:300000
详见 node:http 模块中的 server.requestTimeout。
server.setTimeout(msecs?, callback?): void
详见 node:http 模块中的 server.setTimeout()。
- 类型:
number默认值: 0(无超时)
详见 node:http 模块中的 server.timeout。
- 类型:
number默认值:5000(5 秒)
详见 node:http 模块中的 server.keepAliveTimeout。
https.createServer(options?, requestListener?): void
// curl -k https://localhost:8000/ import { createServer } from 'node:https'; import { readFileSync } from 'node:fs'; const options = { key: readFileSync('private-key.pem'), cert: readFileSync('certificate.pem'), }; createServer(options, (req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000);
// curl -k https://localhost:8000/ const https = require('node:https'); const fs = require('node:fs'); const options = { key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem'), }; https.createServer(options, (req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000);
或
import { createServer } from 'node:https'; import { readFileSync } from 'node:fs'; const options = { pfx: readFileSync('test_cert.pfx'), passphrase: 'sample', }; createServer(options, (req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000);
const https = require('node:https'); const fs = require('node:fs'); const options = { pfx: fs.readFileSync('test_cert.pfx'), passphrase: 'sample', }; https.createServer(options, (req, res) => { res.writeHead(200); res.end('hello world\n'); }).listen(8000);
要为此示例生成证书和密钥,请运行:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ -keyout private-key.pem -out certificate.pem
然后,要为此示例生成 pfx 证书,请运行:
openssl pkcs12 -certpbe AES-256-CBC -export -out test_cert.pfx \ -inkey private-key.pem -in certificate.pem -passout pass:sample
https.get(options, callback?): void
https.get(url, options?, callback?): void
类似于 http.get(),但用于 HTTPS。
options 可以是一个对象、一个字符串或一个 URL 对象。如果 options 是字符串,它会自动被 new URL() 解析。如果它是 URL 对象,它将自动转换为普通 options 对象。
import { get } from 'node:https'; import process from 'node:process'; get('https://encrypted.google.com/', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); }).on('error', (e) => { console.error(e); });
const https = require('node:https'); https.get('https://encrypted.google.com/', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); }).on('error', (e) => { console.error(e); });
所有 HTTPS 客户端请求的 https.Agent 全局实例。与默认 https.Agent 配置的不同之处在于启用了 keepAlive 且 timeout 为 5 秒。
https.request(options, callback?): void
https.request
History
clientCertEngine 选项在运行时已弃用。
clientCertEngine 选项依赖于 OpenSSL 中的自定义引擎支持,该支持在 OpenSSL 3 中已弃用。
当使用 URL 对象时,解析后的用户名和密码现在将进行正确的 URI 解码。
现在接受 highWaterMark 选项。
现在可以连同单独的 options 对象一起传递 url 参数。
options 参数现在可以包含 clientCertEngine。
options 参数可以是 WHATWG URL 对象。
https.request(url, options?, callback?): void
向安全 Web 服务器发出请求。
还接受来自 tls.connect() 的以下附加 options:
ca, cert, ciphers, clientCertEngine (已弃用), crl, dhparam, ecdhCurve,
honorCipherOrder, key, passphrase, pfx, rejectUnauthorized,
secureOptions, secureProtocol, servername, sessionIdContext,
highWaterMark。
options 可以是一个对象、一个字符串或一个 URL 对象。如果 options 是字符串,它将使用 new URL() 自动解析。如果它是 URL 对象,它将自动转换为普通 options 对象。
https.request() 返回 http.ClientRequest 类的一个实例。ClientRequest 实例是一个可写流。如果需要使用 POST 请求上传文件,则写入 ClientRequest 对象。
import { request } from 'node:https'; import process from 'node:process'; const options = { hostname: 'encrypted.google.com', port: 443, path: '/', method: 'GET', }; const req = request(options, (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); }); req.on('error', (e) => { console.error(e); }); req.end();
const https = require('node:https'); const options = { hostname: 'encrypted.google.com', port: 443, path: '/', method: 'GET', }; const req = https.request(options, (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on('data', (d) => { process.stdout.write(d); }); }); req.on('error', (e) => { console.error(e); }); req.end();
使用 tls.connect() 中的选项示例:
const options = { hostname: 'encrypted.google.com', port: 443, path: '/', method: 'GET', key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem'), }; options.agent = new https.Agent(options); const req = https.request(options, (res) => { // ... });
或者,通过不使用 Agent 来选择不启用连接池。
const options = { hostname: 'encrypted.google.com', port: 443, path: '/', method: 'GET', key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem'), agent: false, }; const req = https.request(options, (res) => { // ... });
使用 URL 作为 options 的示例:
const options = new URL('https://abc:[email protected]'); const req = https.request(options, (res) => { // ... });
证书指纹上的示例固定,或公钥上的示例固定(类似于
pin-sha256):
import { checkServerIdentity } from 'node:tls'; import { Agent, request } from 'node:https'; import { createHash } from 'node:crypto'; function sha256(s) { return createHash('sha256').update(s).digest('base64'); } const options = { hostname: 'github.com', port: 443, path: '/', method: 'GET', checkServerIdentity: function(host, cert) { // 确保证书是颁发给我们所连接的主机的 const err = checkServerIdentity(host, cert); if (err) { return err; } // 锁定公钥,类似于 HPKP pin-sha256 锁定 const pubkey256 = 'SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8='; if (sha256(cert.pubkey) !== pubkey256) { const msg = 'Certificate verification error: ' + `The public key of '${cert.subject.CN}' ` + 'does not match our pinned fingerprint'; return new Error(msg); } // 锁定确切的证书,而不是公钥 const cert256 = 'FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:' + '0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65'; if (cert.fingerprint256 !== cert256) { const msg = 'Certificate verification error: ' + `The certificate of '${cert.subject.CN}' ` + 'does not match our pinned fingerprint'; return new Error(msg); } // 此循环仅用于提供信息。 // 打印链中所有证书的证书和公钥指纹 //。通常在公共互联网上锁定颁发者的公钥, // 而在敏感环境中锁定服务的公钥。 let lastprint256; do { console.log('Subject Common Name:', cert.subject.CN); console.log(' Certificate SHA256 fingerprint:', cert.fingerprint256); const hash = createHash('sha256'); console.log(' Public key ping-sha256:', sha256(cert.pubkey)); lastprint256 = cert.fingerprint256; cert = cert.issuerCertificate; } while (cert.fingerprint256 !== lastprint256); }, }; options.agent = new Agent(options); const req = request(options, (res) => { console.log('All OK. Server matched our pinned cert or public key'); console.log('statusCode:', res.statusCode); res.on('data', (d) => {}); }); req.on('error', (e) => { console.error(e.message); }); req.end();
const tls = require('node:tls'); const https = require('node:https'); const crypto = require('node:crypto'); function sha256(s) { return crypto.createHash('sha256').update(s).digest('base64'); } const options = { hostname: 'github.com', port: 443, path: '/', method: 'GET', checkServerIdentity: function(host, cert) { // 确保证书是颁发给我们所连接的主机的 const err = tls.checkServerIdentity(host, cert); if (err) { return err; } // 锁定公钥,类似于 HPKP pin-sha256 锁定 const pubkey256 = 'SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8='; if (sha256(cert.pubkey) !== pubkey256) { const msg = 'Certificate verification error: ' + `The public key of '${cert.subject.CN}' ` + 'does not match our pinned fingerprint'; return new Error(msg); } // 锁定确切的证书,而不是公钥 const cert256 = 'FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:' + '0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65'; if (cert.fingerprint256 !== cert256) { const msg = 'Certificate verification error: ' + `The certificate of '${cert.subject.CN}' ` + 'does not match our pinned fingerprint'; return new Error(msg); } // 此循环仅用于提供信息。 // 打印链中所有证书的证书和公钥指纹 //。通常在公共互联网上锁定颁发者的公钥, // 而在敏感环境中锁定服务的公钥。 do { console.log('Subject Common Name:', cert.subject.CN); console.log(' Certificate SHA256 fingerprint:', cert.fingerprint256); hash = crypto.createHash('sha256'); console.log(' Public key ping-sha256:', sha256(cert.pubkey)); lastprint256 = cert.fingerprint256; cert = cert.issuerCertificate; } while (cert.fingerprint256 !== lastprint256); }, }; options.agent = new https.Agent(options); const req = https.request(options, (res) => { console.log('All OK. Server matched our pinned cert or public key'); console.log('statusCode:', res.statusCode); res.on('data', (d) => {}); }); req.on('error', (e) => { console.error(e.message); }); req.end();
例如输出:
Subject Common Name: github.com Certificate SHA256 fingerprint: FD:6E:9B:0E:F3:98:BC:D9:04:C3:B2:EC:16:7A:7B:0F:DA:72:01:C9:03:C5:3A:6A:6A:E5:D0:41:43:63:EF:65 Public key ping-sha256: SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8= Subject Common Name: Sectigo ECC Domain Validation Secure Server CA Certificate SHA256 fingerprint: 61:E9:73:75:E9:F6:DA:98:2F:F5:C1:9E:2F:94:E6:6C:4E:35:B6:83:7C:E3:B9:14:D2:24:5C:7F:5F:65:82:5F Public key ping-sha256: Eep0p/AsSa9lFUH6KT2UY+9s1Z8v7voAPkQ4fGknZ2g= Subject Common Name: USERTrust ECC Certification Authority Certificate SHA256 fingerprint: A6:CF:64:DB:B4:C8:D5:FD:19:CE:48:89:60:68:DB:03:B5:33:A8:D1:33:6C:62:56:A8:7D:00:CB:B3:DE:F3:EA Public key ping-sha256: UJM2FOhG9aTNY0Pg4hgqjNzZ/lQBiMGRxPD5Y2/e0bw= Subject Common Name: AAA Certificate Services Certificate SHA256 fingerprint: D7:A7:A0:FB:5D:7E:27:31:D7:71:E9:48:4E:BC:DE:F7:1D:5F:0C:3E:0A:29:48:78:2B:C8:3E:E0:EA:69:9E:F4 Public key ping-sha256: vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM= All OK. Server matched our pinned cert or public key statusCode: 200