HTTPS#

稳定性: 2 - 稳定

源代码: lib/https.js

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 时,如果代码有可能在未启用加密支持的 Node.js 版本上运行时,请考虑使用 import() 函数,而不是词法 import 关键字。

let https;
try {
  https = await import('node:https');
} catch (err) {
  console.error('https support is disabled!');
} 

类:https.Agent#

http.Agent 类似的 HTTPS 的 Agent 对象。 更多信息请参见 https.request()

new Agent([options])#

  • options <Object> 要在代理上设置的可配置选项集。 可以具有与 http.Agent(options) 相同的字段,以及
    • maxCachedSessions <number> TLS 缓存会话的最大数量。 使用 0 禁用 TLS 会话缓存。 默认: 100

    • servername <string> 要发送到服务器的 服务器名称指示扩展的值。 使用空字符串 '' 禁用发送扩展。 默认: 目标服务器的主机名,除非目标服务器是使用 IP 地址指定的,在这种情况下,默认值为 ''(无扩展)。

      有关 TLS 会话重用的信息,请参见 会话恢复

事件:'keylog'#
  • line <Buffer> ASCII 文本行,采用 NSS SSLKEYLOGFILE 格式。
  • 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#

更多信息请参见 http.Server

server.close([callback])#

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

server[Symbol.asyncDispose]()#

稳定性: 1 - 实验性的

调用 server.close() 并返回一个 promise,该 promise 在服务器关闭时实现。

server.closeAllConnections()#

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

server.closeIdleConnections()#

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

server.headersTimeout#

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

server.listen()#

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

server.maxHeadersCount#

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

server.requestTimeout#

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

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

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

server.timeout#

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

server.keepAliveTimeout#

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

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

// 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])#

类似于 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#

用于所有 HTTPS 客户端请求的 https.Agent 的全局实例。 与默认的 https.Agent 配置不同之处在于,它启用了 keepAlive,并且 timeout 为 5 秒。

https.request(options[, callback])#

https.request(url[, options][, 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() 的选项的示例

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:xyz@example.com');

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) {
    // Make sure the certificate is issued to the host we are connected to
    const err = checkServerIdentity(host, cert);
    if (err) {
      return err;
    }

    // Pin the public key, similar to HPKP pin-sha256 pinning
    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);
    }

    // Pin the exact certificate, rather than the pub key
    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);
    }

    // This loop is informational only.
    // Print the certificate and public key fingerprints of all certs in the
    // chain. Its common to pin the public key of the issuer on the public
    // internet, while pinning the public key of the service in sensitive
    // environments.
    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) {
    // Make sure the certificate is issued to the host we are connected to
    const err = tls.checkServerIdentity(host, cert);
    if (err) {
      return err;
    }

    // Pin the public key, similar to HPKP pin-sha256 pinning
    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);
    }

    // Pin the exact certificate, rather than the pub key
    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);
    }

    // This loop is informational only.
    // Print the certificate and public key fingerprints of all certs in the
    // chain. Its common to pin the public key of the issuer on the public
    // internet, while pinning the public key of the service in sensitive
    // environments.
    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