mit einem Klick
leak-audit
修改日志输出、错误响应格式、配置文件、协议字符串常量、部署配置后触发。
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Menü
修改日志输出、错误响应格式、配置文件、协议字符串常量、部署配置后触发。
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Basierend auf der SOC-Berufsklassifikation
写入新模块文档、重构模块、或用户要求分析模块时触发。
Bug 修复或对接问题排查确认有效后,将经验记录到知识库。
修改 PMR 分配器、内存池配置、热路径容器、对象生命周期管理代码后触发。
编写或修改性能基准测试、分析 benchmark 结果、优化热路径性能时触发。
新增或修改 enable_shared_from_this 类、co_spawn 调用、shared_ptr 捕获的 lambda、co_await 后的成员访问时触发。
新增或修改原子操作、异步定时器、无锁通道、跨线程状态同步、CAS 竞争相关代码时触发。
| name | leak-audit |
| description | 修改日志输出、错误响应格式、配置文件、协议字符串常量、部署配置后触发。 |
在修改涉及协议处理、日志输出、错误响应、配置文件、部署配置代码后,必须对变更部分执行以下审计清单。审查系统通过收集代理软件泄漏的唯一标识符、版本字符串、错误格式、二进制格式规律、服务端指纹等建立黑名单。
审查系统的信息收集是多层次的:
server=xxx)、版本字符串、自定义错误格式。这些是确定性的黑名单匹配。协议交互中的明文字符串是审查系统的首要检测目标。任何可唯一标识代理软件实现的字符串都会被收录进黑名单。
| 检查项 | 说明 |
|---|---|
| server 标识 | 协议交互中是否包含可识别的软件标识?HTTP 响应头中的 Server: xxx、TLS 扩展中的自定义标识、WebSocket 子协议名称、HTTP/2 Settings 帧中的自定义参数,都会暴露实现身份。审查系统维护已知代理软件的 Server header 值黑名单 |
| 版本字符串 | 是否在协议交互中暴露软件版本号?版本字符串是已知的泄漏风险。即使版本号不直接暴露在协议中,版本升级导致的行为变化(如新增扩展、参数微调)也会暴露版本信息 |
| 错误消息格式 | 错误响应是否使用与主流软件一致的格式?自研的错误格式(如 {"error": "auth_failed", "code": 1001})本身就是指纹。标准 Web 服务器(Nginx、Apache)的错误响应格式是公开的,任何偏差都可被识别 |
| 默认端口/路径 | 是否使用常见的代理默认端口或路径?如 :1080(SOCKS)、:8080(HTTP 代理)、:443 上的 /secret-path。审查系统对常见代理端口和路径进行主动扫描 |
| 自定义 header | 是否在 HTTP 响应中添加自定义 header?如 X-Proxy-Version、X-Auth-Status。任何非标准 header 都是确定性的指纹 |
| WebSocket 子协议 | 如果使用 WebSocket,子协议名称是否为不可识别的通用名称?代理软件特有的子协议名称(如 proxy-v2、tunnel)是已知的检测特征 |
| ALPN 协议标识 | ALPN 协商中使用的协议标识是否与伪装目标一致?自定义 ALPN 值(如 proxy-proto)会被审查系统记录 |
审查系统不仅检测当前版本的指纹,还会持续追踪软件版本变化。如果某次升级改变了协议行为(如 TLS 扩展顺序、HTTP/2 Settings 参数、帧格式细节),审查系统可以通过"行为突变"识别出正在升级的部署节点,并关联同一软件的不同版本。
| 检查项 | 说明 |
|---|---|
| 行为一致性 | 软件升级后,协议行为是否保持一致?如果某次升级改变了 TLS 扩展顺序或帧格式,审查系统可以通过行为变化追踪软件版本和部署规模。升级期间全网节点同时发生相同的行为变化 = 全局版本指纹 |
| 配置驱动行为 | 协议行为是否由配置驱动而非硬编码?硬编码的行为参数意味着所有部署实例共享同一指纹。配置驱动使得不同部署可以有不同的行为特征,避免全网统一指纹 |
| 去标识化 | 配置协商中是否包含可追踪的版本信息?即使不暴露具体版本号,版本号字段的存在(如 v=2)本身就是版本特征 — 审查系统可以通过该字段的出现/消失追踪版本升级。应使用不可识别的协商方式(如通过密钥派生推断版本) |
| 参数版本绑定 | 协议参数是否与版本号强绑定?如果新版本引入了新的默认参数(如不同的 HTTP/2 Settings 值),这些参数差异本身就是版本标识。应确保参数可配置且默认值在配置层面随机化 |
| 升级过渡期 | 软件升级的过渡期间,新旧版本节点是否同时在线?审查系统可以检测到同一 IP 段内节点行为的分化(一部分用旧行为、一部分用新行为),从而推断正在进行版本升级。灰度发布策略应考虑指纹一致性 |
每种代理软件实现都有独特的行为"签名"。审查系统通过探测这些独特行为来识别实现身份,即使没有任何字符串泄漏。
| 检查项 | 说明 |
|---|---|
| 错误响应行为 | 审查系统可以构造特定的错误触发条件(发送畸形数据、截断的握手包、错误的认证信息),不同代理实现在同一条件下的错误响应各不相同 — 有的返回自定义错误帧、有的直接 RST、有的静默关闭、有的返回标准 TLS Alert。如果错误响应的行为模式可唯一映射到某个已知实现,实现身份暴露 |
| 认证失败行为 | 认证失败时服务端的行为是否与标准 Web 服务器一致?标准 Web 服务器不会在认证失败时发送任何自定义错误帧。如果代理软件在认证失败时发送自定义错误帧(即使是加密的),该帧的长度/格式/时序本身就是实现指纹 |
| 解析错误行为 | 发送不符合协议规范的数据时,服务端的响应是否与标准服务器一致?如发送不完整的 TLS 记录、错误的 TLS 版本号、无效的 ALPN 值。不同实现在这些边界情况的处理方式不同,构成实现指纹 |
| 检查项 | 说明 |
|---|---|
| 协议规范灰色地带 | 在协议规范的灰色地带(如未定义的行为、可选字段的处理方式),不同实现的处理方式不同。审查系统可以探测这些边界条件来识别实现。如:收到不支持的 TLS 扩展时是忽略还是返回错误?收到 ALPN 不匹配时是继续还是断开? |
| 资源耗尽行为 | 发送大量并发连接或超大请求时,服务端的资源耗尽行为是否与标准服务器一致?不同实现在资源耗尽时的表现不同 — 有的返回 503、有的 RST、有的静默丢弃。这种行为差异是可识别的 |
| 超时行为 | 不同实现在各种超时场景下的行为是否一致?如:TCP Keep-Alive 超时、TLS 握手超时、HTTP 请求超时。超时值和超时后的连接关闭方式都是实现指纹 |
| 检查项 | 说明 |
|---|---|
| 响应时间分布 | 不同实现在处理特定大小/数量的请求时的性能特征(响应时间分布、内存使用模式)是可区分的。审查系统可以通过统计分析建立性能特征基线,与已知实现的性能特征进行匹配 |
| 并发处理特征 | 并发连接数增加时,响应时间的退化模式是可区分的。基于协程的实现和基于线程池的实现,在并发压力下的响应时间分布曲线形状不同 |
| 内存分配模式 | 内存分配频率和大小分布通过响应时间的微抖动间接暴露。基于竞技场分配器和基于通用堆分配器的实现,在大量短连接场景下的响应时间方差不同 |
| 检查项 | 说明 |
|---|---|
| 开源代码指纹 | 如果代码开源,审查系统可以直接从代码中提取行为模式。关键路径的行为是否可以通过代码分析精确预测?考虑在关键路径引入不可预测的随机化(如随机的错误响应延迟、随机的帧填充) |
| 确定性 vs 随机化 | 错误响应是否完全确定性?如果所有错误响应都是确定性的(相同的输入总是产生相同的输出),审查系统可以精确匹配。引入受控随机化可以破坏精确匹配,但需要确保随机化本身不引入新的指纹 |
| 编译产物指纹 | 即使源代码不公开,编译后的二进制文件也有独特的指纹(代码段大小、函数调用模式、字符串常量)。混淆和 strip 可以增加逆向难度,但不能完全消除 |
| 二进制编译指纹 | 编译后的二进制文件是否有独特的指纹(代码段大小、函数调用模式、字符串常量、编译器优化特征)?即使源代码不公开,二进制分析仍可提取实现特征。建议:启用 strip、移除符号表、移除 RTTI、使用 LTO 优化消除函数边界。注意:混淆和 strip 增加逆向难度但不能完全消除指纹 |
日志是信息泄漏的高风险通道。日志文件可能被入侵者获取、被运维人员误操作泄露、被日志收集系统传输到不安全的环境。
| 检查项 | 说明 |
|---|---|
| 密钥材料不记录 | 日志中是否包含密钥材料(auth_key、shared_secret、private_key、session_key、AEAD nonce)?即使以十六进制编码,密钥材料一旦进入日志就视为已泄漏。日志系统不得记录任何密钥的原始值或派生中间值 |
| 用户数据不记录 | 日志中是否包含用户密码、请求内容、请求体、响应体等敏感信息?代理服务器转发的流量内容不得出现在日志中 |
| IP 地址脱敏 | 日志中的客户端/目标 IP 是否需要脱敏?在生产环境中,IP 地址可能关联到具体用户。至少对最后一段进行掩码处理(如 192.168.1.xxx),或使用不可逆哈希 |
| 日志级别控制 | Debug 日志是否仅在开发环境启用?生产环境不得输出调试级别的详细协议信息。调试日志通常包含内部状态、协议字段细节、处理流程,这些信息对审查系统极具价值。确保生产构建的日志级别 >= info |
| SNI 记录 | 日志中记录的 SNI 是否包含用户访问的真实目标域名?SNI 暴露用户的访问目标,如果日志文件泄露,审查系统可以通过 SNI 记录推断代理的使用模式。生产环境应避免记录 SNI 原始值 |
| 日志格式统一 | 日志格式是否与标准服务器软件一致?自定义日志格式如果外泄可能成为指纹。标准格式(如 Nginx 的 combined log format)不包含代理特有的字段 |
| 日志时间戳精度 | 日志时间戳的精度是否过高?毫秒级精度的协议事件时间戳可能泄露处理时间侧信道。审查系统可以通过分析日志时间戳推断出认证成功/失败路径的时间差异、帧处理的复杂度差异。生产环境的日志时间戳精度不应高于秒级 |
| 错误详情脱敏 | 日志中的错误信息是否包含内部实现细节?如 "AEAD decryption failed: tag mismatch at offset 42" 这样的信息暴露了内部使用了 AEAD 加密。错误日志应使用通用描述 |
| 日志文件权限 | 日志文件是否设置了适当的文件权限?日志文件应仅对服务进程和管理员可读,不得对其他用户可读 |
| 日志轮转与清理 | 日志是否定期轮转和清理?长期积累的日志文件是信息泄漏的高风险目标。日志轮转策略应确保旧日志在合理时间内被清理或归档加密 |
错误响应是审查系统建立实现指纹的核心数据源。标准 Web 服务器在错误场景下的行为是高度标准化的,任何偏差都可被识别。
| 检查项 | 说明 |
|---|---|
| HTTP 错误格式 | HTTP 错误响应是否使用标准格式?不得包含自定义 header 或 body 标识。如 400 Bad Request 的响应必须与 Nginx/Apache 的格式完全一致 — 相同的 header 顺序、相同的 body 内容、相同的换行符 |
| 协议错误遵循 RFC | 协议错误响应是否严格遵循对应 RFC 格式?如 SOCKS5 错误必须使用 RFC 1928 定义的回复格式,不得添加自定义字段。HTTP/2 RST_STREAM 必须使用标准错误码 |
| TLS Alert 标准码 | TLS Alert 消息是否使用标准错误码?自定义错误码会暴露代理实现。特别注意:BoringSSL 和 OpenSSL 在相同条件下的 Alert 描述码不同 — 如 MAC 验证失败时,BoringSSL 使用 bad_record_mac(20) 而 OpenSSL 使用 decrypt_error(51)。如果 ClientHello 声称是 Chrome(使用 BoringSSL),但 TLS Alert 返回了 OpenSSL 的错误码,这种不匹配直接暴露伪装。必须确保 TLS 库的 Alert 行为与声称的身份一致 |
| 连接重置行为 | 异常断开时是否发送标准 TCP RST 而非自定义错误帧?自定义错误帧(即使是加密的)在长度和时序上都是指纹。标准 Web 服务器的异常断开行为是:发送 TLS close_notify 或直接 TCP RST |
| 错误响应时序 | 错误响应的延迟是否与标准服务器一致?异常快(< 1ms,无真实处理开销)或异常慢(有额外认证/解密开销)的响应都是指纹。认证失败路径和认证成功路径的响应时间差异是已知的时序侧信道 |
| 错误页面内容 | 错误响应的 body 内容是否与标准服务器(如 Nginx/Apache)一致?空 body 或自定义 HTML 是指纹。标准 Web 服务器的错误页面包含特定的 HTML 结构、CSS 样式、错误描述文本。如果使用伪装,错误页面必须与伪装目标完全一致 |
| 错误响应一致性 | 相同类型的错误是否总是返回完全相同的响应(包括 header 顺序、空格、换行)?微小的格式差异(如 Content-Type: text/html vs Content-Type:text/html,Server:nginx vs Server: nginx)是服务端软件指纹。响应格式必须完全固定化 |
| 错误响应大小 | 错误响应的总大小(包括 header 和 body)是否与标准服务器一致?异常小的错误响应(如仅有状态行没有 body)或异常大的错误响应都是指纹 |
| HTTP/2 错误帧 | HTTP/2 的 RST_STREAM 和 GOAWAY 帧是否使用标准错误码?自定义错误码或非标准的额外帧都是指纹。HTTP/2 错误响应的帧序列必须与标准服务器完全一致 |
配置文件是凭据泄漏和指纹泄漏的双重风险源。
| 检查项 | 说明 |
|---|---|
| 凭据不硬编码 | 配置文件中的密码、密钥是否以哈希形式存储?不得明文存储。密码应以 HKDF 派生的哈希存储,密钥应通过密钥管理系统获取而非写在配置文件中 |
| 配置文件权限 | 配置文件是否设置了适当的文件权限?配置文件包含敏感凭据,应仅对服务进程可读(如 0600)。不得对其他用户或组可读 |
| 默认配置无示例凭据 | 默认配置是否包含示例凭据?用户可能忘记修改。默认配置应使用空值或占位符(如 "password": ""),不得包含可工作的示例值(如 "password": "changeme") |
| 配置文件不入版本控制 | 配置文件是否已加入版本控制忽略列表?不得将包含凭据的配置文件提交到版本控制。应提供不含敏感信息的模板配置(如 config.example.json) |
| 密钥材料格式 | 密钥在配置文件中的编码格式是否泄漏实现信息?如使用 Base64 编码的密钥与使用十六进制编码的密钥,格式本身可能暗示底层实现。应使用协议中常见的编码方式 |
| 配置注释安全 | 配置文件中的注释是否包含敏感信息?如 // temporary password for testing、// TODO: change before production。注释中不得包含任何凭据或部署信息 |
| 环境变量传递 | 敏感配置是否通过环境变量传递而非文件?环境变量在进程启动后可以清除,比文件形式更安全。但环境变量在进程列表中可见,需权衡利弊 |
JA4S 是针对 TLS ServerHello 的指纹算法,捕获服务端的 TLS 行为特征。如果服务端的 JA4S 指纹与声称伪装的目标服务器类型不匹配,伪装直接暴露。
| 检查项 | 说明 |
|---|---|
| JA4S 指纹构成 | JA4S 指纹由以下部分构成:TLS 版本 + 密码套件 + 扩展数量 + 扩展列表哈希。这些字段的组合必须与伪装目标的服务端行为完全一致。如声称伪装 Nginx + BoringSSL,则 JA4S 指纹必须与真实 Nginx + BoringSSL 的 JA4S 指纹匹配 |
| 密码套件选择 | ServerHello 中选择的密码套件是否与目标服务器类型的偏好一致?不同服务器软件的密码套件选择偏好不同 — Nginx 默认偏好 ECDHE+AESGCM,Apache 默认偏好 ECDHE+CHACHA20。选择偏好的差异产生不同的 JA4S 指纹 |
| 扩展数量与内容 | ServerHello 中返回的扩展数量和内容是否与目标服务器类型一致?不同 TLS 库返回不同的扩展集合。如 BoringSSL 和 OpenSSL 在 ServerHello 中返回的扩展列表不同(BoringSSL 通常返回更少的扩展) |
| Alert 描述码精度 | TLS Alert 的描述码是否与声称的 TLS 库完全一致?BoringSSL 和 OpenSSL 在相同条件下的 Alert 描述码不同。这是 JA4S 指纹之外的服务端行为指纹。如:MAC 验证失败 → BoringSSL 返回 bad_record_mac(20),OpenSSL 返回 decrypt_error(51)。如果 ClientHello 声称 Chrome(BoringSSL),但 Alert 返回了 OpenSSL 的错误码,伪装暴露 |
| NewSessionTicket 行为 | TLS 1.3 的 NewSessionTicket 消息的行为(是否发送、发送数量、ticket lifetime、early_data 扩展)是否与目标服务器类型一致?不同服务器软件的 session resumption 策略不同 |
| 自测建议 | 是否定期计算自身的 JA4S 指纹并与目标服务器类型的已知 JA4S 指纹进行比对?建议建立自动化测试:在 CI 中计算 JA4S 指纹,与目标服务器类型的指纹库进行匹配验证 |
| 版本追踪 | JA4S 指纹是否随 TLS 库版本更新而变化?如果代理软件的 JA4S 指纹与当前主流版本的浏览器/TLS 库不一致(如停留在旧版 Chrome 的指纹),"永不更新的 Chrome"本身就是异常 |
审查系统不仅检测单个节点的指纹,还会通过全局指纹关联估算部署规模。
| 检查项 | 说明 |
|---|---|
| 去关联化 | 不同部署实例的行为是否足够不同?如果所有部署实例共享完全相同的指纹(TLS 扩展顺序、HTTP/2 参数、错误页面格式),审查系统可以统计全网部署规模:扫描全网发现该指纹的 IP 数量 = 部署节点总数 |
| 实例唯一性 | 每个部署实例是否可以生成独立的随机化参数?如随机化的 HTTP/2 Settings 值、TLS 扩展顺序微调、错误页面的微小变化。这些随机化使得每个实例的指纹略有不同,审查系统无法通过指纹关联所有部署实例 |
| 全局指纹避免 | 是否存在任何"所有部署实例都相同"的标识?审查系统可以通过全局指纹的出现频率估算部署规模。常见的全局指纹包括:固定的 HTTP/2 Settings 参数、固定的 TLS 扩展顺序、固定的错误响应格式、固定的 ALPN 回退行为 |
| 配置驱动随机化 | 随机化参数是否从配置文件中的种子派生?种子应该是实例特定的(如从机器 ID、IP 地址、或管理员指定的随机种子派生),而非所有实例共享同一默认种子。共享种子 = 共享指纹 = 全局可追踪 |
| 随机化稳定性 | 实例的随机化参数在重启后是否保持一致?如果每次重启都重新生成随机化参数,审查系统可以发现同一 IP 的指纹频繁变化,这本身就是异常行为。应从配置中的持久种子派生随机化参数 |
| 指纹分散度 | 不同实例之间的指纹差异是否足够分散?如果差异仅限于少数参数(如仅 HTTP/2 Settings 不同,其他全部相同),审查系统仍可通过不变部分关联实例。应确保足够多的参数可随机化 |
| 规模化测试 | 是否验证过大量部署实例的指纹分散度?模拟 100+ 实例的指纹分布,确认审查系统无法通过指纹聚类识别出部署规模 |
以下审计领域在其他专项 skill 中有更深入的覆盖:
traffic-audit — 涵盖固定长度模式、字段分界可识别性、填充破坏、熵值分布等crypto-audit 第 6 节 — 涵盖 CN/SAN、颁发者、有效期、序列号、CT Log、签名算法等dpi-audit 第 10 节 — 涵盖 TLS 扩展顺序、HTTP/2 Settings、TCP 窗口大小、TTL、TCP 选项顺序等dpi-audit 第 11 节 — 涵盖帧头魔数、命令编码、TLS 记录层合规性等probe-audit — 涵盖回落机制形式化安全、跨协议探测防御、时序侧信道、多阶段探针协调维度security-audit — 提供安全审计 skills 的编排指南,确定修改特定代码时应按何种顺序执行哪些审计在变更文件中搜索所有硬编码的字符串,特别关注协议交互中的标识符。
分析错误路径、边界条件行为是否可唯一映射到当前实现。
如果变更改变了协议行为,评估版本追踪暴露风险。
逐行检查新增/修改的日志调用,确认不包含敏感信息。
确认所有错误响应使用标准格式,延迟和内容与标准服务器一致。
确认凭据不以明文存储,默认配置不含示例凭据。
检查是否存在全局统一的指纹,评估审查系统是否能统计全网部署规模。
// ❌ 协议中暴露软件标识
auto settings_text = std::string("v=2\nserver=myproxy\n");
// 审查系统直接匹配 "server=myproxy" 关键词
// ❌ 版本号字段 — 即使值不含版本信息,字段名本身是版本追踪点
auto handshake = std::string("version=2\nmethod=aes-256-gcm\n");
// ❌ 自定义 header 暴露实现
co_await send("HTTP/1.1 407 Proxy Auth Required\r\nX-Proxy-Version: 2.1\r\n\r\n");
// ✅ 使用不可识别的标识或省略不必要的标识
// 协议协商通过密钥派生或隐式推断完成,不暴露任何可读标识
// ❌ 日志中记录密钥材料
trace::debug("auth_key: {}", to_hex(auth_key));
trace::debug("shared_secret: {}", to_hex(shared_secret));
trace::debug("session_key derived: nonce={}", to_hex(nonce));
// ✅ 日志中仅记录操作结果,不记录任何密钥值
trace::info("authentication successful");
trace::info("key derivation completed");
// ❌ 自定义错误响应 — 格式本身就是指纹
co_await send("HTTP/1.1 407 Proxy Authentication Required\r\n"
"X-Proxy: MyProxy\r\n"
"Content-Type: text/plain\r\n\r\n"
"Proxy authentication failed. Code: 1001\n");
// ✅ 标准错误响应 — 与 Nginx 格式完全一致
co_await send("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Server: nginx\r\n"
"Date: Wed, 28 May 2025 00:00:00 GMT\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 184\r\n"
"Connection: keep-alive\r\n\r\n"
"<html>\r\n<head><title>407 Proxy Authentication Required</title></head>\r\n"
"<body>\r\n<center><h1>407 Proxy Authentication Required</h1></center>\r\n"
"<hr><center>nginx</center>\r\n</body>\r\n</html>\r\n");
// ❌ 所有部署实例共享完全相同的指纹 — 审查系统可统计全网规模
// 所有实例使用完全相同的 HTTP/2 Settings 参数
constexpr auto header_table_size = 4096;
constexpr auto max_concurrent_streams = 100;
constexpr auto initial_window_size = 65535;
// ❌ 共享默认随机化种子 — 所有实例生成相同的随机化参数
auto seed = default_config_seed; // 所有实例的默认种子相同
auto randomized_params = derive_from_seed(seed);
// ✅ 每个部署实例使用配置中的独立种子
auto seed = config.instance_seed; // 管理员为每个实例指定不同的种子
auto randomized_params = derive_seed(seed);
// ✅ 从实例特定信息派生种子
auto seed = hash(config.server_address + config.server_port + config.admin_secret);
auto randomized_params = derive_seed(seed);
// ❌ 错误响应的微小格式差异暴露实现身份
// 调用 1: "HTTP/1.1 400 Bad Request\r\nServer:nginx\r\n" (无空格)
// 调用 2: "HTTP/1.1 400 Bad Request\r\nServer: nginx\r\n" (有空格)
// 格式不固定 = 两次响应可被关联为同一非标准实现
// ✅ 错误响应格式严格遵循标准(如 Nginx 的格式),且固定不变
// 使用统一的格式化函数,确保每次生成的错误响应字节级一致
auto response = format_error(400, "Bad Request");
// response 的每个字节在多次调用中完全相同
// ❌ TLS Alert 描述码与声称的 TLS 库不匹配
// ClientHello 声称 Chrome(BoringSSL),但 Alert 返回了 OpenSSL 的描述码
// MAC 验证失败 → 返回 decrypt_error(51)
// BoringSSL 应该返回 bad_record_mac(20)
// ✅ 确保 TLS 库的 Alert 行为与声称的身份一致
// 如果伪装 BoringSSL/Chrome,所有 Alert 必须使用 BoringSSL 的描述码
// bad_record_mac(20), handshake_failure(40), illegal_parameter(47) ...
// 如果伪装 OpenSSL/Nginx,使用 OpenSSL 的描述码
// decrypt_error(51), handshake_failure(40), illegal_parameter(47) ...
// ❌ 生产代码中使用 debug 级别记录协议细节
trace::debug("TLS handshake completed: cipher={}, ext_count={}, alpn={}",
cipher_suite, extensions.size(), alpn);
// 这些信息如果泄露到日志文件,暴露了完整的 TLS 行为指纹
// ✅ 使用 info 级别记录操作摘要,不包含协议字段细节
trace::info("TLS handshake completed");
// ❌ 日志时间戳精度过高
trace::debug("packet processed in {}us", elapsed_microseconds);
// 微秒级时间戳暴露处理时间侧信道
// ✅ 生产环境日志时间戳不高于秒级,不记录微秒级处理时间
trace::info("session completed");