首页
/ Puppeteer `SecurityDetails.subjectName()`:读取 HTTPS 证书主体名称的完整指南

Puppeteer `SecurityDetails.subjectName()`:读取 HTTPS 证书主体名称的完整指南

2026-09-07 14:50:16作者:翟萌耘Ralph

本文面向使用 Puppeteer(JavaScript API for Chrome and Firefox)进行 HTTPS 页面安全审计与响应分析的开发者,系统讲解 SecurityDetails.subjectName() 方法的签名、返回值语义、底层数据来源,并结合仓库源码与测试用例给出可直接运行的实战示例。读完本文,你将能够通过 HTTPResponse.securityDetails() 获取 TLS 证书的 subjectName、issuer、SAN、有效期与协议信息,并准确判断其适用边界(仅限安全连接、且依赖浏览器协议支持)。

SecurityDetails 与 subjectName 的定位

在 Puppeteer 中,SecurityDetails 类 代表「通过安全连接接收到的响应」的安全详情对象。类的公开文档注释原文为:

The SecurityDetails class represents the security details of a response that was received over a secure connection.

该类封装了一条 HTTPS(TLS)响应对应服务端证书的核心元数据,构造器被标记为 @internal实现文件 位于 packages/puppeteer-core/src/common/SecurityDetails.ts。因此第三方代码不能直接 new SecurityDetails(...),只能通过响应对象公开的访问器获得实例。

subjectName() 是 SecurityDetails 公开的六个方法之一,对应文档其余方法还包括:

subjectName() 方法签名与语义

目标文档 docs/api/puppeteer.securitydetails.subjectname.md 给出的完整签名如下:

class SecurityDetails {
  subjectName(): string;
}

方法不接收任何参数,返回类型为 string。其语义原文为:

The name of the subject to which the certificate was issued.

(证书签发对象——即证书主体 Subject——的名称。)

对照源码注释可确认同一表述(SecurityDetails.ts):

/**
 * The name of the subject to which the certificate was issued.
 */
subjectName(): string {
  return this.#subjectName;
}

在 X.509 证书中,Subject(主体)字段标识证书所颁发给的实体,通常由通用名称(CN)、组织(O)、组织单位(OU)等相对可识别名称(RDN)构成。对于常规 Web 服务器证书,subjectName 一般就是域名或服务器主机名;但在本仓库测试中,测试自签名证书的 subject 被设置为 puppeteer-tests(见下文测试章节),说明该方法返回的是证书 Subject 的完整字符串表达,并不必然等于站点域名。若需同时核验证书覆盖的域名,应配合 subjectAlternativeNames() 返回的 SAN 列表一起判断。

数据从哪里来:CDP 与 WebDriver BiDi 两条通道

从源码结构看,SecurityDetails 的实例数据并非 Puppeteer 自行解析,而是由底层浏览器协议传入的负载构造。构造器接收的是 Protocol.Network.SecurityDetails 类型(devtools-protocol 定义的 CDP 结构),并逐字段拷贝(SecurityDetails.ts):

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;
}

#subjectName 对应的正是 CDP 负载中的 securityPayload.subjectName 字段。两条传输通道的接线位置不同:

  1. CDP 通道:Chrome/Firefox 走 Chromium 调试协议。在 packages/puppeteer-core/src/cdp/HTTPResponse.ts 中,构造 HTTPResponse 时判断 responsePayload.securityDetails 是否存在:存在则 new SecurityDetails(responsePayload.securityDetails),否则为 null。也就是说,CDP 的 Network.responseReceived 事件中 securityDetails 字段为空(如 HTTP 明文连接)时,整个 SecurityDetails 都为空。

  2. WebDriver BiDi 通道:Puppeteer 同样支持 WebDriver BiDi 协议(参见 webdriver-bidi 指南)。在 packages/puppeteer-core/src/bidi/HTTPResponse.ts 中,安全详情来自响应数据里非标准的 goog:securityDetails 扩展字段,并且额外要求 cdpSupported 为真才会构造 SecurityDetails 实例。

开发者可以据此推断:调用 subjectName() 的可靠性取决于所用连接协议是否提供完整的安全详情负载,BiDi 模式下若无 CDP 支持则拿不到该信息。

如何拿到 SecurityDetails:通过 HTTPResponse.securityDetails()

subjectName() 是 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;

两个关键约束:

  • 返回值是 SecurityDetails | null只有当响应来自安全连接时才有对象;对普通 HTTP 响应(如 http:// 页面)返回 null,必须先判空再访问 subjectName()
  • 获取方式通常是 page.goto(...) 的返回值,或监听 page.on('response', ...) 事件取到 HTTPResponse 后调用。

securityDetails() 的完整用法与签名可参考 HTTPResponse.securityDetails 文档(该接口在 puppeteer.httpresponse.md 中与 url()status()headers() 等同级列出)。

实战示例:抓取 HTTPS 响应的证书主体

下面给出一个可运行的最小示例,演示如何从一次 HTTPS 导航中读取 subjectName()。仓库内自带基于 @puppeteer/browsers 的测试 HTTPS 服务与自签名证书(见 testserver),本地对任意真实 HTTPS 站点运行同样适用:

import puppeteer from 'puppeteer';

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

// 方式一:直接使用 page.goto 的返回值
const response = await page.goto('https://example.com');
const details = response.securityDetails();
if (details) {
  console.log('证书主体(Subject Name):', details.subjectName());
  console.log('签发者(Issuer):        ', details.issuer());
  console.log('安全协议(Protocol):    ', details.protocol());
  console.log('起始有效期(Valid From):', new Date(details.validFrom() * 1000).toISOString());
  console.log('结束有效期(Valid To):  ', new Date(details.validTo() * 1000).toISOString());
  console.log('SAN 列表:              ', details.subjectAlternativeNames());
} else {
  console.log('该响应不是安全连接(HTTP 或无 securityDetails),subjectName 不可用');
}

// 方式二:监听所有子资源响应的安全详情
page.on('response', res => {
  const sd = res.securityDetails();
  if (sd) {
    console.log(`${res.url()} -> subject: ${sd.subjectName()}`);
  }
});

await page.goto('https://example.com');
await browser.close();

要点归纳:

  • 先判空再访问。securityDetails() 对非 HTTPS 返回 null,此时调用 subjectName() 会抛 TypeError
  • validFrom() / validTo() 返回 Unix 秒级时间戳,展示前应乘以 1000 转为毫秒(文档见 validfromvalidto)。
  • 判断证书与站点域名是否匹配时,应将 subjectName()subjectAlternativeNames() 结合使用。

用仓库测试印证行为边界

仓库测试文件 test/src/acceptInsecureCerts.test.ts 以可验证断言方式锁定了 subjectName() 的行为,是理解该方法语义最直接的证据:

const securityDetails = response!.securityDetails()!;
expect(securityDetails.issuer()).toBe('puppeteer-tests');
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',
]);

测试揭示的边界行为可总结为三方面:

  1. HTTPS 响应必有 SecurityDetails:第一个用例在自定义浏览器实例(acceptInsecureCerts: true)中访问 httpsServer.EMPTY_PAGE,随后断言 subjectName() 等于测试证书的 Subject puppeteer-tests。这证明该方法读取的是证书 Subject 原始值。
  2. 非安全请求返回 null:第二个用例 should be |null| for non-secure requests 访问普通 HTTP 服务器并断言 response.securityDetails()null——即 HTTP 响应无法读取 subjectName(test/src/acceptInsecureCerts.test.ts)。
  3. 重定向响应同样携带详情:第三个用例验证 302 重定向的中间响应也能报告 SecurityDetails,说明导航链路中的每个安全响应均可读取证书信息(test/src/acceptInsecureCerts.test.ts)。

此外,测试需在 acceptInsecureCerts: true 的自建浏览器中运行,这也提示了一个使用前提:如果目标站点使用自签名或不受信任的证书,需要像 acceptInsecureCerts 相关测试 那样在 launch 选项中放开证书校验(launchOptionslaunchoptions 文档),否则导航本身就会失败,根本拿不到响应对象。

常见问题小结

  • 为什么 HTTPS 页面下 securityDetails() 有时仍返回 null? 除了页面本身不是 HTTPS 之外,还可能是采用了 WebDriver BiDi 且 CDP 能力未启用(见上文 BiDi 通道的 cdpSupported 分支),或该子资源响应未携带完整安全信息。编码时一律先判空即可。
  • subjectName()subjectAlternativeNames() 有何区别? 前者返回证书 Subject 字段(证书签发给谁的「登记名称」),后者返回证书覆盖的域名/IP 列表。现代浏览器校验域名主要依赖 SAN,因此两者都应检查。
  • 能否解析出 CN、O 等细分字段? Puppeteer 未提供结构化解析,subjectName() 返回的就是协议负载中的原始字符串。如需要进一步拆分 RDN,可自行做字符串解析。

围绕本方法还常配合使用 protocol()(确认 TLS 版本)与 issuer()(确认证书链签发方),三者组合即可在浏览器自动化场景下完成对目标站点 TLS 证书的快速核验。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 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
531
594
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.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388