Skip to content

HTTPS

[稳定: 2 - 稳定]

稳定: 2 稳定性: 2 - 稳定

源代码: lib/https.js

HTTPS 是基于 TLS/SSL 的 HTTP 协议。在 Node.js 中,它作为单独的模块实现。

判断是否缺少加密支持

Node.js 可以在不包含 node:crypto 模块支持的情况下构建。在这种情况下,尝试从 https 模块导入或调用 require('node:https') 将导致抛出错误。

使用 CommonJS 时,可以使用 try/catch 语句捕获抛出的错误:

js
let https
try {
  https = require('node:https')
} catch (err) {
  console.error('https 支持已禁用!')
}

使用词法 ESM import 关键字时,只有在 尝试加载模块(例如,使用预加载模块)之前 注册了 process.on('uncaughtException') 处理程序的情况下,才能捕获该错误。

使用 ESM 时,如果代码有可能在未启用加密支持的 Node.js 版本上运行,请考虑使用 import() 函数而不是词法 import 关键字:

js
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) 相同的字段,以及
    • maxCachedSessions <数字> TLS 缓存会话的最大数量。使用 0 禁用 TLS 会话缓存。默认值: 100
    • servername <字符串> 要发送到服务器的 服务器名称指示扩展 的值。使用空字符串 '' 禁用发送扩展。默认值: 目标服务器的主机名,除非目标服务器使用 IP 地址指定,在这种情况下,默认值为 ''(无扩展)。有关 TLS 会话重用的信息,请参见 会话恢复

事件:'keylog'

新增于:v13.2.0, v12.16.0

  • line <Buffer> ASCII 文本行,采用 NSS SSLKEYLOGFILE 格式。
  • tlsSocket <tls.TLSSocket> 生成该事件的 tls.TLSSocket 实例。

当此代理管理的连接生成或接收密钥材料时,会发出 keylog 事件(通常在握手完成之前,但不一定)。此密钥材料可用于调试,因为它允许解密捕获的 TLS 流量。对于每个套接字,它可能会发出多次。

一个典型的用例是将接收到的行追加到一个公共文本文件中,该文件稍后由软件(例如 Wireshark)用来解密流量:

js
// ...
https.globalAgent.on('keylog', (line, tlsSocket) => {
  fs.appendFileSync('/tmp/ssl-keys.log', line, { mode: 0o600 })
})

类:https.Server

新增于:v0.3.4

更多信息请参见 http.Server

server.close([callback])

新增于:v0.1.90

参见 node:http 模块中的 server.close()

server[Symbol.asyncDispose]()

新增于:v20.4.0

[稳定性:1 - 实验性]

稳定性:1 稳定性:1 - 实验性

调用 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

参见 node:http 模块中的 server.headersTimeout

server.listen()

启动 HTTPS 服务器以监听加密连接。此方法与 net.Server 中的 server.listen() 方法相同。

server.maxHeadersCount

请参见 node:http 模块中的 server.maxHeadersCount

server.requestTimeout

[历史]

版本变更
v18.0.0默认请求超时时间从无超时更改为 300 秒(5 分钟)。
v14.11.0新增于:v14.11.0

请参见 node:http 模块中的 server.requestTimeout

server.setTimeout([msecs][, callback])

新增于:v0.11.2

请参见 node:http 模块中的 server.setTimeout()

server.timeout

[历史]

版本变更
v13.0.0默认超时时间从 120 秒更改为 0(无超时)。
v0.11.2新增于:v0.11.2

参见 node:http 模块中的 server.timeout

server.keepAliveTimeout

新增于:v8.0.0

参见 node:http 模块中的 server.keepAliveTimeout

https.createServer([options][, requestListener])

新增于:v0.3.4

js
// 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)
js
// 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)

或者

js
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)
js
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)

要为本示例生成证书和密钥,请运行:

bash
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \
  -keyout private-key.pem -out certificate.pem

然后,要为本示例生成 pfx 证书,请运行:

bash
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.0options 参数可以是 WHATWG URL 对象。
v0.3.6新增于:v0.3.6

类似于 http.get(),但用于 HTTPS。

options 可以是对象、字符串或 URL 对象。如果 options 是字符串,它将使用 new URL() 自动解析。如果它是 URL 对象,它将自动转换为普通的 options 对象。

js
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)
})
js
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.0clientCertEngine 选项依赖于 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.0options 参数现在可以包含 clientCertEngine
v7.5.0options 参数可以是 WHATWG URL 对象。
v0.3.6新增于:v0.3.6

向安全的 Web 服务器发出请求。

还接受来自 tls.connect() 的以下附加 optionscacertciphersclientCertEngine(已弃用)、crldhparamecdhCurvehonorCipherOrderkeypassphrasepfxrejectUnauthorizedsecureOptionssecureProtocolservernamesessionIdContexthighWaterMark

options 可以是对象、字符串或 URL 对象。如果 options 是字符串,则会使用 new URL() 自动解析它。如果它是 URL 对象,它将自动转换为普通的 options 对象。

https.request() 返回 http.ClientRequest 类的实例。ClientRequest 实例是可写流。如果需要使用 POST 请求上传文件,则写入 ClientRequest 对象。

js
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()
js
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 的示例:

js
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 来选择退出连接池。

js
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 的示例:

js
const options = new URL('https://abc:')

const req = https.request(options, res => {
  // ...
})

证书指纹或公钥上的示例固定(类似于 pin-sha256):

js
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()
js
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()

示例输出:

text
主体通用名称: 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