HTTPS
源代码: lib/https.js
HTTPS 是基于 TLS/SSL 的 HTTP 协议。在 Node.js 中,它作为单独的模块实现。
判断是否缺少加密支持
Node.js 可以在不包含 node:crypto
模块支持的情况下构建。在这种情况下,尝试从 https
模块导入或调用 require('node:https')
将导致抛出错误。
使用 CommonJS 时,可以使用 try/catch 语句捕获抛出的错误:
let https
try {
https = require('node:https')
} catch (err) {
console.error('https 支持已禁用!')
}
使用词法 ESM import
关键字时,只有在 在 尝试加载模块(例如,使用预加载模块)之前 注册了 process.on('uncaughtException')
处理程序的情况下,才能捕获该错误。
使用 ESM 时,如果代码有可能在未启用加密支持的 Node.js 版本上运行,请考虑使用 import()
函数而不是词法 import
关键字:
let https
try {
https = await import('node:https')
} catch (err) {
console.error('https 支持已禁用!')
}
类: https.Agent
[历史]
版本 | 变更 |
---|---|
v5.3.0 | 支持 0 maxCachedSessions 来禁用 TLS 会话缓存。 |
v2.5.0 | 为 TLS 会话重用向 options 添加了参数 maxCachedSessions 。 |
v0.4.5 | 在 v0.4.5 中添加 |
与 http.Agent
类似的 HTTPS Agent
对象。更多信息请参见 https.request()
。
new Agent([options])
[历史]
版本 | 变更 |
---|---|
v12.5.0 | 如果目标主机使用 IP 地址指定,则不会自动设置服务器名称。 |
options
<对象> 在代理上设置的可配置选项集。可以具有与http.Agent(options)
相同的字段,以及
事件:'keylog'
新增于:v13.2.0, v12.16.0
line
<Buffer> ASCII 文本行,采用 NSSSSLKEYLOGFILE
格式。tlsSocket
<tls.TLSSocket> 生成该事件的tls.TLSSocket
实例。
当此代理管理的连接生成或接收密钥材料时,会发出 keylog
事件(通常在握手完成之前,但不一定)。此密钥材料可用于调试,因为它允许解密捕获的 TLS 流量。对于每个套接字,它可能会发出多次。
一个典型的用例是将接收到的行追加到一个公共文本文件中,该文件稍后由软件(例如 Wireshark)用来解密流量:
// ...
https.globalAgent.on('keylog', (line, tlsSocket) => {
fs.appendFileSync('/tmp/ssl-keys.log', line, { mode: 0o600 })
})
类:https.Server
新增于:v0.3.4
- 继承自: <tls.Server>
更多信息请参见 http.Server
。
server.close([callback])
新增于:v0.1.90
callback
<Function>- 返回值:<https.Server>
参见 node:http
模块中的 server.close()
。
server[Symbol.asyncDispose]()
新增于:v20.4.0
调用 server.close()
并返回一个 promise,该 promise 在服务器关闭时 fulfilled。
server.closeAllConnections()
新增于:v18.2.0
参见 node:http
模块中的 server.closeAllConnections()
。
server.closeIdleConnections()
新增于:v18.2.0
参见 node:http
模块中的 server.closeIdleConnections()
。
server.headersTimeout
新增于:v11.3.0
- <number> 默认值:
60000
参见 node:http
模块中的 server.headersTimeout
。
server.listen()
启动 HTTPS 服务器以监听加密连接。此方法与 net.Server
中的 server.listen()
方法相同。
server.maxHeadersCount
- <number> 默认值:
2000
请参见 node:http
模块中的 server.maxHeadersCount
。
server.requestTimeout
[历史]
版本 | 变更 |
---|---|
v18.0.0 | 默认请求超时时间从无超时更改为 300 秒(5 分钟)。 |
v14.11.0 | 新增于:v14.11.0 |
- <number> 默认值:
300000
请参见 node:http
模块中的 server.requestTimeout
。
server.setTimeout([msecs][, callback])
新增于:v0.11.2
msecs
<number> 默认值:120000
(2 分钟)callback
<Function>- 返回值: <https.Server>
请参见 node:http
模块中的 server.setTimeout()
。
server.timeout
[历史]
版本 | 变更 |
---|---|
v13.0.0 | 默认超时时间从 120 秒更改为 0(无超时)。 |
v0.11.2 | 新增于:v0.11.2 |
- <数字> 默认值: 0(无超时)
参见 node:http
模块中的 server.timeout
。
server.keepAliveTimeout
新增于:v8.0.0
- <数字> 默认值:
5000
(5 秒)
参见 node:http
模块中的 server.keepAliveTimeout
。
https.createServer([options][, requestListener])
新增于:v0.3.4
options
<对象> 接受来自tls.createServer()
、tls.createSecureContext()
和http.createServer()
的options
。requestListener
<函数> 要添加到'request'
事件的监听器。- 返回值: <https.Server>
// 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])
https.get(url[, options][, callback])
[历史]
版本 | 变更 |
---|---|
v10.9.0 | 现在可以同时传递 url 参数和单独的 options 对象。 |
v7.5.0 | options 参数可以是 WHATWG URL 对象。 |
v0.3.6 | 新增于:v0.3.6 |
url
<字符串> | <URL>options
<对象> | <字符串> | <URL> 接受与https.request()
相同的options
,方法默认设置为 GET。callback
<函数>
类似于 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.globalAgent
[历史]
版本 | 变更 |
---|---|
v19.0.0 | 代理现在默认使用 HTTP Keep-Alive 和 5 秒超时。 |
v0.5.9 | 新增于:v0.5.9 |
所有 HTTPS 客户端请求的 https.Agent
全局实例。通过启用 keepAlive
并设置 5 秒的 timeout
与默认的 https.Agent
配置有所不同。
https.request(options[, callback])
https.request(url[, options][, callback])
[历史]
版本 | 变更 |
---|---|
v22.4.0, v20.16.0 | clientCertEngine 选项依赖于 OpenSSL 中的自定义引擎支持,该支持在 OpenSSL 3 中已弃用。 |
v16.7.0, v14.18.0 | 使用 URL 对象解析的用户名和密码现在将被正确地 URI 解码。 |
v14.1.0, v13.14.0 | 现在接受 highWaterMark 选项。 |
v10.9.0 | 现在可以与单独的 options 对象一起传递 url 参数。 |
v9.3.0 | options 参数现在可以包含 clientCertEngine 。 |
v7.5.0 | options 参数可以是 WHATWG URL 对象。 |
v0.3.6 | 新增于:v0.3.6 |
options
<对象> | <字符串> | <URL> 接受来自http.request()
的所有options
,但默认值有所不同:protocol
默认值:'https:'
port
默认值:443
agent
默认值:https.globalAgent
callback
<函数>
向安全的 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()
中的 options 的示例:
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:')
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 = '证书验证错误:' + `'${cert.subject.CN}' 的公钥` + '与我们的固定指纹不匹配'
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 = '证书验证错误:' + `'${cert.subject.CN}' 的证书` + '与我们的固定指纹不匹配'
return new Error(msg)
}
// 此循环仅供参考。
// 打印链中所有证书的证书和公钥指纹。在公共互联网上固定发行者的公钥很常见,而在敏感环境中则固定服务的公钥。
let lastprint256
do {
console.log('主体通用名称:', cert.subject.CN)
console.log(' 证书 SHA256 指纹:', cert.fingerprint256)
const hash = createHash('sha256')
console.log(' 公钥 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('一切正常。服务器与我们固定的证书或公钥匹配')
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 = '证书验证错误:' + `'${cert.subject.CN}' 的公钥` + '与我们的固定指纹不匹配'
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 = '证书验证错误:' + `'${cert.subject.CN}' 的证书` + '与我们的固定指纹不匹配'
return new Error(msg)
}
// 此循环仅供参考。
// 打印链中所有证书的证书和公钥指纹。在公共互联网上固定发行者的公钥很常见,而在敏感环境中则固定服务的公钥。
do {
console.log('主体通用名称:', cert.subject.CN)
console.log(' 证书 SHA256 指纹:', cert.fingerprint256)
hash = crypto.createHash('sha256')
console.log(' 公钥 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('一切正常。服务器与我们固定的证书或公钥匹配')
console.log('statusCode:', res.statusCode)
res.on('data', d => {})
})
req.on('error', e => {
console.error(e.message)
})
req.end()
示例输出:
主体通用名称: github.com
证书 SHA256 指纹: 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
公钥 ping-sha256: SIXvRyDmBJSgatgTQRGbInBaAK+hZOQ18UmrSwnDlK8=
主体通用名称: Sectigo ECC Domain Validation Secure Server CA
证书 SHA256 指纹: 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
公钥 ping-sha256: Eep0p/AsSa9lFUH6KT2UY+9s1Z8v7voAPkQ4fGknZ2g=
主体通用名称: USERTrust ECC Certification Authority
证书 SHA256 指纹: 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
公钥 ping-sha256: UJM2FOhG9aTNY0Pg4hgqjNzZ/lQBiMGRxPD5Y2/e0bw=
主体通用名称: AAA Certificate Services
证书 SHA256 指纹: 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
公钥 ping-sha256: vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM=
一切正常。服务器与我们固定的证书或公钥匹配
statusCode: 200