ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Envoy JA4 指纹 ALPN 十六进制编码修复:`ja4_alpn_hex_conversion_fix` 运行时开关解析

Envoy JA4 指纹 ALPN 十六进制编码修复:`ja4_alpn_hex_conversion_fix` 运行时开关解析 Envoy JA4 指纹 ALPN 十六进制编码修复ja4_alpn_hex_conversion_fix运行时开关解析【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy导读本文围绕 Envoy 当前版本变更日志中的一条 bug 修复changelogs/current/bug_fixes/ja4__alpn_non_alphanumeric.rst展开JA4 指纹生成在遇到非字母数字non-alphanumericALPN 字符时原先未按 JA4 规范将其编码为符合规范的十六进制值该问题现由运行时开关envoy.reloadable_features.ja4_alpn_hex_conversion_fix控制修复。读完本文你将掌握JA4 指纹的完整生成格式与%TLS_JA4_FINGERPRINT%格式化指令的用法、旧实现与新实现针对 ALPN 字符的编码差异、该运行时开关的配置方式以及底层源码实现与测试用例的证据位置便于在实际运维和二次开发中对照使用。变更内容概述原变更日志条目即本文的关联文档全文如下Fixed a bug where JA4 fingerprint generation did not correctly encode non-alphanumeric ALPN characters as spec-compliant hexadecimal values. The fix is guarded by the runtime flagenvoy.reloadable_features.ja4_alpn_hex_conversion_fix.逐句拆解其技术要点Bug 位置JA4 指纹生成逻辑具体位于 TLS Inspector 监听器过滤器内的JA4Fingerprinter实现source/extensions/filters/listener/tls_inspector/ja4_fingerprint.cc。Bug 现象当 ClientHello 中的 ALPN 协议名首字符或末字符不是字母/数字如包含-、_、/、*等符号时生成的指纹编码不符合 JA4 规范。修复方式改为输出符合 JA4 规范的十六进制编码。可控性修复由运行时开关envoy.reloadable_features.ja4_alpn_hex_conversion_fix保护便于灰度发布与回滚。JA4 指纹与 ALPN 字符在其中的作用JA4JARM AdvancedFoxIO 提出的 TLS 指纹方案是对 JA3 的改进。Envoy 在 ja4_fingerprint.h 的类注释中给出了其内部格式定义tXXdYYZZ_CIPHERHASH_EXTENSIONHASH各字段含义如下字段说明t协议类型t表示 TLSq表示 QUICd表示 DTLS当前实现仅处理 TLS固定为tXXTLS 版本13、12、11、10等d/iSNI 是否存在d 存在域名i 无 SNIYY密码套件数量2 位数字ZZ扩展数量2 位数字_CIPHERHASH密码套件列表 SHA-256 哈希的前 12 个十六进制字符_EXTENSIONHASH扩展列表 SHA-256 哈希的前 12 个十六进制字符ALPNApplication-Layer Protocol Negotiation字符是ZZ之后的两个字符即getJA4AlpnChars()的返回值取客户端 ALPN 协议列表中第一个协议名的首字符和该协议名的末字符在 ja4_fingerprint.cc 中实现std::string JA4Fingerprinter::getJA4AlpnChars(const SSL_CLIENT_HELLO* ssl_client_hello) { // 从 ClientHello 的 ALPN 扩展TLSEXT_TYPE_application_layer_protocol_negotiation读取协议列表 // ... char first proto[0]; char last proto[proto.length() - 1]; // ... 根据运行时开关决定编码方式 return absl::StrFormat(%c%c, first, last); }常见的h2HTTP/2、http/1.1等协议名全部由字母数字组成首末字符直接按原样输出即可。但当协议名首末字符为非字母数字如-、_、/、*、:等时直接输出原字符会破坏 JA4 指纹的字符集约定JA4 规范要求指纹仅含小写字母与数字这正是本次修复要解决的问题。Bug 根因旧实现与非规范十六进制编码在开启修复开关之前旧逻辑位于 ja4_fingerprint.cc 的else分支} else { if (!absl::ascii_isalnum(first) || !absl::ascii_isalnum(last)) { // Convert to hex if non-alphanumeric return absl::StrFormat(%02x%02x, static_castuint8_t(first), static_castuint8_t(last)); } }问题在于%02x%02x会把首、末字符各自的完整字节值转换为两个十六进制数每个 1 字节 → 2 个 hex 字符最终输出4 个 hex 字符。而 JA4 规范要求 ALPN 部分始终恰好是 2 个字符字母数字原样输出非字母数字以 2 个 hex 字符编码。因此旧实现存在两类偏差输出长度错误非字母数字场景下输出了 4 个 hex 字符破坏了tXXdYYZZ后紧跟 2 字符 ALPN 段的固定布局导致后续字段解析错位、指纹长度不统一不符合规范语义JA4 规范对 ALPN 段ZZ之后的两字符定义为“首字符的高半字节nibble与末字符的低半字节拼成 1 字节再以 2 个 hex 字符表示”旧实现并未遵循这一拼装规则。修复实现符合 JA4 规范的 2 字符十六进制编码开启运行时开关后走 ja4_fingerprint.cc 的新分支if (Runtime::runtimeFeatureEnabled(envoy.reloadable_features.ja4_alpn_hex_conversion_fix)) { if (!absl::ascii_isalnum(first) || !absl::ascii_isalnum(last)) { // Per JA4 spec, output exactly 2 hex chars for non-alphanumeric ALPN values: // Extract the high nibble ( 4) of the first byte and low nibble ( 0x0F) of the last byte. return absl::StrFormat(%x%x, (static_castuint8_t(first) 4) 0x0F, static_castuint8_t(last) 0x0F); } }新实现严格按 JA4 规范编码取首字符的高半字节(static_castuint8_t(first) 4) 0x0F即第一个字节右移 4 位后取低 4 位取末字符的低半字节static_castuint8_t(last) 0x0F即最后一个字节的低 4 位两者各格式化为 1 个 hex 字符%x%x组合起来恰好 2 个 hex 字符与字母数字场景的输出宽度保持一致。例如假设 ALPN 协议名首字符为-ASCII 0x2D末字符为/ASCII 0x2F则旧实现输出2d2f4 字符非规范新实现输出22首字符高半字节0x2 末字符低半字节0xf→2f恰好 2 字符。这样无论 ALPN 协议名是否包含特殊符号JA4 指纹的 ALPN 段始终固定为 2 个字符保证整体指纹格式严格符合t[0-9]{2}[di][0-9]{2}[0-9]{2}[0-9a-z]{2}_[0-9a-f]{12}_[0-9a-f]{12}的规范模式该正则模式也出现在 test/extensions/filters/listener/tls_inspector/ja4_fingerprint_test.cc 的测试注释中。值得注意的是无论开关是否开启当首末字符均为字母数字时两者都走absl::StrFormat(%c%c, first, last)原样输出修复只影响“至少一端非字母数字”的边界场景对绝大多数携带h2、http/1.1等常规 ALPN 的客户端行为完全不变因此默认开启该开关对存量指纹数据的影响面极小。运行时开关envoy.reloadable_features.ja4_alpn_hex_conversion_fix该开关在 source/common/runtime/runtime_features.cc 中通过RUNTIME_GUARD宏注册RUNTIME_GUARD(envoy_reloadable_features_ja4_alpn_hex_conversion_fix);RUNTIME_GUARD是 Envoy 运行时特性体系的统一声明机制会在启动时自动生成默认开启的布尔特性。其判定结果通过 source/common/runtime/runtime_features.h 暴露给调用方由 ja4_fingerprint.cc 中的Runtime::runtimeFeatureEnabled(...)读取。如何配置该开关用户可通过 Envoy 的运行时配置文件--runtime指定的文件目录或 admin 接口/runtime覆盖默认值。典型配置片段如下runtime: symlink_root: /srv/runtime/current # 在 /srv/runtime/current/envoy/reloadable_features/ja4_alpn_hex_conversion_fix 文件中写入 # true - 启用新编码符合 JA4 规范默认 # false - 回退旧编码%02x%02x 四字符输出即在运行时根目录下创建envoy/reloadable_features/ja4_alpn_hex_conversion_fix文件内容为true或false。默认值为trueRUNTIME_GUARD 特性默认开启因此无需任何配置即可获得修复后的行为若在灰度升级期间需要与旧版本 Envoy 保持一致的指纹输出可临时将该文件内容置为false实现回滚待全量升级完成后再删除覆盖。修复在指纹生成链路中的位置JA4 指纹的整体生成入口是 ja4_fingerprint.cc 的JA4Fingerprinter::create()它按顺序拼接各段std::string JA4Fingerprinter::create(const SSL_CLIENT_HELLO* ssl_client_hello) { return absl::StrCat( t, // 协议类型当前仅 TLS getJA4TlsVersion(ssl_client_hello), // TLS 版本 hasSNI(ssl_client_hello) ? d : i, // SNI 是否存在 formatTwoDigits(countCiphers(ssl_client_hello)),// 密码套件数量 formatTwoDigits(countExtensions(ssl_client_hello)), // 扩展数量 getJA4AlpnChars(ssl_client_hello), // ALPN 首末字符本次修复点 _, getJA4CipherHash(ssl_client_hello), // 密码套件 SHA-256 前 12 hex _, getJA4ExtensionHash(ssl_client_hello)); // 扩展 签名算法 SHA-256 前 12 hex }调用关系与配置入口如下配置开关TLS Inspector 监听器过滤器的 proto 配置项enable_ja4_fingerprintingapi/envoy/extensions/filters/listener/tls_inspector/v3/tls_inspector.proto默认false需要显式开启后才计算 JA4 指纹过滤器调用在 tls_inspector.cc 中当enableJA4Fingerprinting()为真时调用JA4Fingerprinter::create()生成指纹并写入 StreamInfo对外输出指纹可通过访问日志格式化指令%TLS_JA4_FINGERPRINT%输出详见 docs/root/configuration/advanced/substitution_formatter.rst。该指令适用于 HTTP/TCP/THRIFT 协议若未使用 TLS 或握手未完成则输出-。# 启用 TLS Inspector 并开启 JA4 指纹 listener_filters: - name: envoy.filters.listener.tls_inspector typed_config: type: type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector enable_ja4_fingerprinting: true # 访问日志中输出 JA4 指纹 access_log: - name: envoy.access_loggers.file typed_config: type: type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog path: /dev/stdout format: %TLS_JA4_FINGERPRINT%\n测试与验证仓库中的单元测试位于 test/extensions/filters/listener/tls_inspector/ja4_fingerprint_test.cc覆盖了以下关键点GREASE 值过滤GreaseValueFiltering验证isNotGrease()对 RFC 8701 定义的 16 个 GREASE 值如0x0a0a、0xaaaa、0xfafa返回false对普通值返回true。GREASE 值在计数、哈希前被剔除避免指纹被客户端随机填充干扰指纹格式集成TlsInspectorJA4IntegrationTest通过 tls_utility.h 的generateClientHello()构造真实的 TLS 1.2/TLS 1.3 ClientHello含 SNI、ALPN 等变体验证数据能够被正确识别例如含 ALPNh2的报文能被检测到其格式期望为正则t[0-9]{2}[di][0-9]{2}[0-9]{2}[0-9a-z]{2}_[0-9a-f]{12}_[0-9a-f]{12}——其中[0-9a-z]{2}正是 ALPN 段要求严格为 2 个小写字母数字字符与本次修复的目标一致。此外构建层面 source/extensions/filters/listener/tls_inspector/BUILD 将ja4_fingerprint.cc/.h单独封装为ja4_fingerprint_lib便于复用与测试也说明该实现是独立可测的模块。升级与回滚建议默认行为新版本中envoy.reloadable_features.ja4_alpn_hex_conversion_fix默认开启指纹生成自动遵循 JA4 规范无需改动配置指纹一致性若你正在使用基于 JA4 指纹的威胁情报/客户端识别系统且客户端 ALPN 中包含非字母数字字符升级后这些客户端的指纹会从旧的 4 字符输出变为新的 2 字符输出需要同步更新下游分析规则回滚路径在多实例滚动升级期间可通过运行时文件将开关临时置为false保持新旧实例输出一致待全部升级完成后移除覆盖值即可验证手段升级后可通过访问日志的%TLS_JA4_FINGERPRINT%抽查指纹确认 ALPN 段恒为 2 字符且整体匹配规范正则再逐步放开下游指纹库的比对。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表