首页
/ Puppeteer SecurityDetails 类解析:从 HTTPS 响应中读取 TLS 证书与安全连接详情

Puppeteer SecurityDetails 类解析:从 HTTPS 响应中读取 TLS 证书与安全连接详情

2026-09-07 16:51:32作者:何举烈Damon

导读

在自动化抓取、接口监控与安全审计场景中,确认目标站点是否走 HTTPS、使用了哪一版 TLS 协议、服务器证书由谁签发、有效期到何时,往往是判断“这条响应是否可信”的第一步。Puppeteer 通过 HTTPResponse.securityDetails() 返回的 SecurityDetails 类,把浏览器(Chrome/Edge 或 Firefox)在建立安全连接时获得的 TLS/证书信息暴露给开发者。本文将围绕 docs/api/puppeteer.securitydetails.md 定义的 SecurityDetails 类,系统讲解它的每个方法、与 CDP 底层协议的数据对应关系、实际获取方式,并辅以仓库源码与测试用例作为验证依据。

SecurityDetails 类的定位与基本签名

SecurityDetails 表示一条“通过安全连接接收到的响应”的安全详情,即该响应所依赖的 TLS 连接对应的证书与协议信息。官方类型声明如下:

export declare class SecurityDetails

它本身不负责建立连接、也不触发任何网络行为,而是对底层传输层安全信息的只读封装。其数据来源是 Chrome DevTools Protocol 中 Network 域随响应一起返回的 Protocol.Network.SecurityDetails 负载。

值得注意的是,其构造函数被标记为 internal:第三方代码不应直接 new SecurityDetails(...),也不应创建继承它的子类。开发者只能通过 HTTPResponse.securityDetails() 间接拿到实例。这一点在 packages/puppeteer-core/src/common/SecurityDetails.ts 的源码注释与实现中均有体现——构造函数接收的是 CDP 协议负载并据此初始化内部字段。

类的成员概览

SecurityDetails 共暴露 6 个取值方法,全部为同步只读方法:

方法 返回类型 含义
issuer() string 证书签发机构(Issuer)名称
protocol() string 使用的安全协议,例如 "TLS 1.2"
subjectAlternativeNames() string[] 证书的主题备用名称(SANs)列表
subjectName() string 证书签发给的主题(Subject)名称
validFrom() number 证书有效期起始的 Unix 时间戳
validTo() number 证书有效期结束的 Unix 时间戳

底层实现:与 CDP 负载的逐字段映射

查看 packages/puppeteer-core/src/common/SecurityDetails.ts 可以发现,类的 6 个私有字段在构造时直接从 Protocol.Network.SecurityDetails 中拷贝:

constructor(securityPayload: Protocol.Network.SecurityDetails) {
  this.#subjectName = securityPayload.subjectName;
  this.#issuer = securityPayload.issuer;
  this.#validFrom = securityPayload.validFrom;
  this.#validTo = securityPayload.validTo;
  this.#protocol = securityPayload.protocol;
  this.#sanList = securityPayload.sanList;
}

也就是说,issuer() 对应 CDP 的 securityDetails.issuersubjectName() 对应 subjectNameprotocol() 对应 protocolvalidFrom()/validTo() 对应各自同名字段,subjectAlternativeNames() 则对应 sanList。六个 getter 只是原样返回各自字段(见同文件 L38-L77)。可以推断,Puppeteer 对这类安全详情字段做的是“原样透传 + 扁平化封装”,并未做额外加工或类型转换。

方法与返回语义逐一说明

issuer()

issuer(): string;

返回证书签发机构(CA)的名称字符串,也就是 X.509 证书 issuer 字段中 CN/O 等部分的体现。在双向认证或自建 CA 的环境中,可用它快速判断证书链的签发源头是否为本机构,从而识别“中间人证书”与“官方证书”。

subjectName()

subjectName(): string;

返回证书被签发给的主体(Subject)名称。它是证书归属方的直接标识:例如站点证书的 Subject 通常包含域名对应的 CN(CN=www.example.com)或组织名。将它和实际访问的 URL 比对,是识别域名与证书不匹配的最简单手段之一。

subjectAlternativeNames()

subjectAlternativeNames(): string[];

返回证书的所有主题备用名称(Subject Alternative Name, SAN)数组。现代 CA 签发的证书基本都依赖 SAN 来声明合法的域名集合(包括通配域名、多域名、IP 等),而 Subject CN 已不再是判定域名归属的主流依据。因此判断证书是否覆盖某个域名,正确做法是把 location.hostname 或目标域名拿去做 SAN 数组的成员匹配

protocol()

protocol(): string;

返回安全连接实际协商使用的协议版本,典型值形如 "TLS 1.2"。它来自 CDP 的 securityDetails.protocol,对应 TLS 握手中服务端与客户端协商出的最终版本,可用于监控站点是否仍停留在过时的 TLS 1.0/1.1、或已升级到 TLS 1.3。

validFrom() 与 validTo()

validFrom(): number;
validTo(): number;

分别返回证书有效期起始与截止的 Unix 时间戳(秒)。二者均为数字类型,可直接用于有效期计算。例如用 new Date(validFrom() * 1000)new Date(validTo() * 1000) 得到可读的时间范围,再与 Date.now() 比较判断证书是否已过期或临近过期——这是自动化证书巡检的核心算法。

如何拿到 SecurityDetails:通过 HTTPResponse

SecurityDetails 从不单独存在,它的获取入口固定在响应对象上。在 packages/puppeteer-core/src/api/HTTPResponse.ts 中定义了抽象方法:

/**
 * {@link SecurityDetails} if the response was received over the
 * secure connection, or `null` otherwise.
 */
abstract securityDetails(): SecurityDetails | null;

两条关键语义:

  • 响应来自 HTTPS/安全连接 时,返回一个非空的 SecurityDetails
  • 响应来自 HTTP 等非安全连接 时,返回 null(这也是与 response.fromCache()ok() 等方法在返回形态上的重要区别——调用前务必判空)。

CDP(Chrome/Firefox)实现

packages/puppeteer-core/src/cdp/HTTPResponse.ts 中,CDP 响应负载若携带 securityDetails 字段则据此构造对象,否则置为 null

this.#securityDetails = responsePayload.securityDetails
  ? new SecurityDetails(responsePayload.securityDetails)
  : null;

securityDetails() 方法随后直接返回该字段(同文件 L115-L117)。

WebDriver BiDi(Firefox)实现

packages/puppeteer-core/src/bidi/HTTPResponse.ts 中,BiDi 协议通过非标准扩展属性 goog:securityDetails 携带同一份 CDP 结构的安全详情数据;只有当会话支持 CDP(cdpSupported)时才解析出 SecurityDetails,否则 securityDetails() 会抛出 UnsupportedOperation(见同文件 L158-L161)。也就是说,使用 WebDriver BiDi 连接时,安全详情能力取决于浏览器/会话对 CDP 扩展属性的支持度,跨浏览器脚本应做好异常兜底。

一个完整的最小示例

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

try {
  const response = await page.goto('https://example.com', {
    waitUntil: 'networkidle2',
  });

  const details = response?.securityDetails();
  if (details === null || details === undefined) {
    console.log('该响应并非来自安全连接(HTTP),无证书信息。');
  } else {
    console.log('Issuer (签发机构):', details.issuer());
    console.log('Subject (证书主体):', details.subjectName());
    console.log('SANs (备用域名):', details.subjectAlternativeNames().join(', '));
    console.log('TLS 协议版本:', details.protocol());

    const from = new Date(details.validFrom() * 1000);
    const to = new Date(details.validTo() * 1000);
    console.log('有效期:', from.toISOString(), '→', to.toISOString());

    const expired = details.validTo() * 1000 < Date.now();
    console.log('证书是否已过期:', expired);
  }
} finally {
  await browser.close();
}

要点提醒:

  • 生产代码务必处理 securityDetails() 返回 null 的情况(非 HTTPS 页面是常态,不是异常);
  • validFrom()/validTo() 单位是秒,转为毫秒需乘 1000 再交给 Date
  • 连接走 WebDriver BiDi 且不支持 CDP 扩展时,需用 try/catch 捕获可能的 UnsupportedOperation

来自仓库测试用例的行为验证

仓库中已有针对 securityDetails 的专门测试,可作为行为契约的参考。

test/src/acceptInsecureCerts.test.ts 中,测试用例在本地 HTTPS 测试服务器(其自签证书 Subject/Issuer 均为 puppeteer-tests)上断言了完整行为:

const securityDetails = response!.securityDetails()!;
expect(securityDetails.issuer()).toBe('puppeteer-tests');
expect(securityDetails.protocol()).toBe(protocol);
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
expect(securityDetails.validFrom()).toBe(1589357069);
expect(securityDetails.validTo()).toBe(1904717069);
expect(securityDetails.subjectAlternativeNames()).toEqual([
  'www.puppeteer-tests.test',
  'www.puppeteer-tests-1.test',
]);
  • 对普通 HTTP 服务器发起的请求,response.securityDetails() 被断言为 null(见同文件 L45-L50),印证了“非安全连接返回 null”的文档语义;
  • 302 重定向场景下,即使最终页面尚未加载完,重定向响应自身同样携带 SecurityDetails(见同文件 L51-L70),说明安全详情是“逐条响应”粒度的,而非页面粒度的。

此外在 test/src/launcher.test.ts 中,连接本地 HTTPS 服务器后也断言了 securityDetails() 非空且 protocol() 与 TLS socket 实际协商的协议一致,进一步证明 protocol() 返回的是真实握手结果。

补充阅读:SecurityDetails 的 6 个方法均有独立 API 文档,可分别查看 puppeteer.securitydetails.issuer.mdpuppeteer.securitydetails.protocol.mdpuppeteer.securitydetails.subjectalternativenames.mdpuppeteer.securitydetails.subjectname.mdpuppeteer.securitydetails.validfrom.mdpuppeteer.securitydetails.validto.md

常见用途与局限

典型应用场景包括:

  • TLS 版本巡检:批量抓取站点后统一检查 protocol(),发现仍在使用 TLS 1.0/1.1 的存量服务;
  • 证书有效期预警:定时任务抓取自身域名,用 validTo() 判断是否临近过期,提前触发续期提醒;
  • 证书内容一致性核对:对比 issuer()subjectName() 与期望值,快速发现被代理/中间人替换证书的可疑连接;
  • 域名覆盖验证:用 subjectAlternativeNames() 校验多域名/通配证书是否真正覆盖目标主机名。

需要注意的局限:

  • SecurityDetails 只覆盖响应建立安全连接的那一跳,无法代表页面上其他资源(脚本、图片、XHR)各自的证书状态,如要做全页面级审计需结合 page.on('response') 逐条检查;
  • 它是纯读取模型,字段映射自 CDP 负载,字段缺失或协议不支持(BiDi 场景)时可能得到 null 或抛出异常;
  • 证书的“吊销状态”(CRL/OCSP)与完整证书链并不在此模型中,这些信息仍需通过底层 TLS 或专业工具获取。

小结

SecurityDetails 是 Puppeteer 暴露 TLS 连接与 X.509 证书信息的统一只读门面:它以 6 个同步方法把 CDP Network.SecurityDetails 负载中的签发者、主体、SAN、协议版本与有效期原样呈现给上层脚本。理解“经 HTTPResponse.securityDetails() 获取、非安全连接返回 null、时间戳以秒计、San 需自行匹配域名”这几个要点之后,你就能用少量代码为站点搭建 TLS 协议、证书有效期与域名覆盖的自动化巡检能力。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388