Skip to content

JA4 does not strip GREASE from signature_algorithms (JA4_c rotates per connection on Chrome 152+) #44

Description

@LeaveMyYard

pkg/ja4/ja4.go filters GREASE out of the TLS version, cipher suites and extension list, but unmarshalSignatureAlgorithm copies the signature-algorithm list verbatim:

// pkg/ja4/ja4.go:253-263
func (j *JA4Fingerprint) unmarshalSignatureAlgorithm(chs *utls.ClientHelloSpec) {
	var algo []uint16
	for _, e := range chs.Extensions {
		if sae, ok := e.(*utls.SignatureAlgorithmsExtension); ok {
			for _, a := range sae.SupportedSignatureAlgorithms {
				algo = append(algo, uint16(a))   // <- no isGREASEUint16 filter
			}
		}
	}
	j.SignatureAlgorithms = algo
}

Compare unmarshalTLSVersion (:116), unmarshalCipherSuites (:142, :189) and unmarshalExtensions (:152, :207), which all filter. It looks like a plain oversight rather than a design choice.

The JA4 specification says the implementation "needs to ignore GREASE values anywhere it sees them", and FoxIO's reference implementation filters the sigalg list (python/common.py:213).

Why it matters now. Chrome began emitting a GREASE value inside signature_algorithms at M152 (kTlsGreaseSigalgs, gating BoringSSL's ssl_get_grease_value(hs, ssl_grease_signature_algorithm)). The value is redrawn on every handshake, so from M152 onward fingerproxy emits a different JA4 for every connection from the same browser. SignatureAlgorithms feeds only the JA4_c hash (fmt.Sprintf("%s_%s", j.Extensions, j.SignatureAlgorithms), :99), so JA4_a and JA4_b are unaffected — which makes it easy to miss.

Measured over 8 cold ClientHellos from Chrome M152–M155: 8 distinct JA4_c values. Recomputed with GREASE stripped from sigalgs and nothing else changed, all 8 collapse to one, t13d1517h2_8daaf6152771_cb7bf5808d99.

The fix is one filter:

	j.SignatureAlgorithms = filterGREASEUint16(algo)

(matching whatever helper shape suits — isGREASEUint16 already exists in the package.)

Happy to send a PR with a regression test if that is useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions