JA4 didn't define SCSVs clearly which caused incompatible implementations
(This is partially redundant with https://github.com/FoxIO-LLC/ja4-nginx-module/issues/10 but I believe worth to create the issue here since the root cause is on the specs rather than in the nginx module implementation, and two repos appear to be maintained by different person. )
In JA4 Technical Details, when defining Number of Ciphers, the doc didn't clarify if Signalling Cipher-Suite Values (SCSVs) should be counted or excluded. At least two SCSVs are involved:
-
TLS_EMPTY_RENEGOTIATION_INFO_SCSVin value of0x00ffdefined in RFC 5746, which is quite commonly seen in real world -
TLS_FALLBACK_SCSVin value of0x5600defined in RFC 7507
In a simple test using curl as client, the ja4 nignx module generated:
- JA4 string: t13i3012h2_002f,0033,0035,0039,003c,003d,0067,006b,009c,009d,009e,009f,1301,1302,1303,c009,c00a,c013,c014,c023,c024,c027,c028,c02b,c02c,c02f,c030,cca8,cca9,ccaa_000a,000b,000d,0015,0016,0017,002b,002d,0031,0033,3374_0403,0503,0603,0807,0808,0809,080a,080b,0804,0805,0806,0401,0501,0601,0303,0301,0302,0402,0502,0602
- JA4 hash: t13i3012h2_1d37bd780c83_ce5650b735ce`
While on the same TLS request, the ja4 plugin in Wireshark generated:
- JA4 string: t13i3112h2_002f,0033,0035,0039,003c,003d,0067,006b,009c,009d,009e,009f,00ff,1301,1302,1303,c009,c00a,c013,c014,c023,c024,c027,c028,c02b,c02c,c02f,c030,cca8,cca9,ccaa_000a,000b,000d,0015,0016,0017,002b,002d,0031,0033,3374_0403,0503,0603,0807,0808,0809,080a,080b,0804,0805,0806,0401,0501,0601,0303,0301,0302,0402,0502,0602
- JA4 hash: t13i3112h2_e8f1e7e78f70_ce5650b735ce
The differences between them are highlighted.
The ambiguous definition also potentially cause a third party implementation producing incompatible values (when using a different language or library, or even different functions in the same library for obtaining cipher suite), pretty like what happened on ja3er.
I encountered the same issue. I found some Golang JA4 implementation (https://github.com/wi1dcard/fingerproxy/blob/master/pkg/ja4/ja4.go) and Wireshark contain the cipher TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff).
But Nginx implementation doesn't include it.
Please explicitly define the action, to prevent the community implementation inconsistency.
@secmobi @tozh Thanks for bringing this up! Do you have a pcap example?
My initial thought is to update the spec to include these in the count and hash when present as they help to identify specific TLS implementations. The downside being that this would increase the number of potential fingerprints a specific library could produce but that's outvalued by the fact that it would tell a better story of what is happening in the connection.
@john-althouse Sorry I can't share a pcap here since pcap is classified data at my end. But as mentioned, this could be easily reproduced by using curl with your nginx module and wireshark plugin.
I could understand there are considerations on include them or not. However, choosing either way would still be better than current undefined status, as this has caused implementation differences that produces unequal hash values.
Hi @john-althouse , I was wondering if there are any updates on the progress of this issue.
I have met the same issue, so I try to upload my pcap here. You can get the JA4 details with filter "frame.number == 1870".
The JA4 in the pcap is t13d4907h2_0d8feac7bc37_7395dae3b2f3, which calculated with 0x00ff. But JA4 in the nginx is t13d4807h2_d6740521f3cb_7395dae3b2f3, which without 0x00ff.
I would appreciate it if you could support this as soon as possible. Thank you for your help!
Hi, @john-althouse What's more, I find the root cause of this issue is that, OPENSSL does not parse SCSV as actual CIPHERs, as the code shown as below:
/* For SSLv2-compat, ignore leading 0-byte. */
c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher, 1);
if (c != NULL) {
if ((c->valid && !sk_SSL_CIPHER_push(sk, c)) ||
(!c->valid && !sk_SSL_CIPHER_push(scsvs, c))) {
So nginx is just get_Client_ciphers by OPENSSL lib and calculate the "wrong" JA4.
I think this issue may occur in any program which based on OPENSSL. So, do we need to calculate JA4 with the "SCSV"?
Yes, the one in the pcap is correct, nginx is wrong. I'll update the spec to include all non-cyphers in the cypher list and calculation, ignoring GREASE, this would include SCSV and reserved ranges FE00-FEFF. Unfortunately, the nginx module will not receive an update for a while as development has paused on that one while we've been focused on development requirements from our customers.
Thanks for bringing this up again and sorry for the delayed response! Feel free to kick me on email or social media if I'm not responding fast enough on here.