這是關於網路指紋識別的兩部分系列的第二部分
上一部分我介紹了有關TLS 指紋識別方法(以及在不同使用者端的指紋有何區別):
https://mp.weixin.qq.com/s/BvotXrFXwYvGWpqHKoj3uQ
和Tls指紋類似也是一種 Web 伺服器可以依賴指紋來識別哪個使用者端。
例如,它可以識別瀏覽器型別和版本,或者是否使用了指令碼(你是真實瀏覽器啊還是ScriptBoy?)。
該方法依賴於 HTTP/2 協定的內部結構,與其更簡單的前身 HTTP/1.1 相比,這些內部結構鮮為人知。
在這篇文章中,我將首先簡要介紹 HTTP/2協定,然後詳細介紹我們可以協定的哪些引數來識別你究竟誰(what are you)!
使用HTTP/1.1協定,使用者端向伺服器傳送文字請求(通常使用 TLS 加密)預設情況下,Chrome 的請求如下所示:
GET / HTTP/1.1
Host: www.wikipedia.org
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
User-Agent包含使用者端的確切版本,雖然可用於識別使用者端。但是很容易被任何 http 庫或命令列工具偽造(地球人都知道)!
HTTP/2 是 HTTP 協定的主要修訂版,從 2015 年左右開始出現。現在大約一半的網站使用 HTTP/2
基本上所有流行的網站都預設使用它!
如何看伺服器端使用的是否是http2協定呢?
在chrome上看是這樣的
在Firefox上看是這樣的
然而,HTTP 協定的應用程式語意沒有改變:它仍然由熟悉的請求/響應模型組成,包括 URI、HTTP 方法、HTTP 檔頭和狀態碼。
HTTP/2 是一種二進位制協定,與文字 HTTP/1.1 不同。HTTP/2 中的訊息由幀組成,有十種不同用途的幀。幀始終是流的一部分。
Stream都是有編號的,從0開始
如上圖:編號為0的Stream包含如下
然後是編號開始遞增,代表了使用者端給伺服器端傳送的實際請求,如上圖為1的Stream:
這裡推薦使用nghttpd,它可以很方便的建立一個http2協定的webserver。
最關鍵的是,讓使用者端請求的時候它能夠直觀的把每一幀都給列印出來(下面會給大家演示)
我將它安裝在wsl的ubuntu機器上,還得自建一個證書,這裡我遇到了一點坑,
避坑指南請看我寫的(wsl建立證書讓chrome瀏覽器識別):
下面就是如何使用nghttpd跑h2協定server
我這裡分別使用如下使用者端來測試
上面介紹到這是使用者端傳送的第一幀,裡面有一些特殊設定
recv SETTINGS frame <length=24, flags=0x00, stream_id=0>
[SETTINGS_HEADER_TABLE_SIZE(0x01):65536]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):1000]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):6291456]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):262144]
recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
[SETTINGS_HEADER_TABLE_SIZE(0x01):65536]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):131072]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):1073741824]
[SETTINGS_ENABLE_PUSH(0x02):0]
recv SETTINGS frame <length=36, flags=0x00, stream_id=0>
[SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
[SETTINGS_ENABLE_PUSH(0x02):0]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[SETTINGS_MAX_FRAME_SIZE(0x05):16384]
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_MAX_HEADER_LIST_SIZE(0x06):65536]
很明顯,根據測試,在SETTINGS Frame幀裡面設定,
不同的使用者端設定的種類和值都是不同的,這使得很容易區分是否是瀏覽器,
而且這個設定不容易控制,可以用於指紋識別!
HTTP/2 實現了一種流控制機制。
流量控制為接收方提供了在每個流的基礎上調節流量的機制。
使用WINDOW_UPDATE大小來實現的
預設視窗大小由SETTINGS幀裡面的 SETTINGS_INITIAL_WINDOW_SIZE中的值控制,
參考上方測試,可以看到 Chrome 使用 6MB (6291456) 而 Firefox 使用 128KB (131072)
當用戶端接收資料時,它可以使用WINDOW_UPDATE框架來調整視窗大小,從而增加其視窗大小。
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=15663105)
Chrome 實際上將連線級視窗大小增加到 15MB (15663105+65535=15MB)
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=12517377)
Firefox 會將其增加到 12MB
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=1073676289)
curl使用 32MB
參考:https://github.com/curl/curl/blob/10cd69623a544c83bae6d90acdf141981ae53174/lib/http2.c
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
(window_size_increment=16777216)
PYTHON 會將其增加到 16MB
所以我們也可以使用該引數用於指紋識別!
這個有點意思了
從廣義上講,HEADERS 包含了 HTTP/1.1 的所有功能,包含了
URI、方法(GET/POST/等)和使用者端的頭等!
下面的幾個偽檔頭的順序對於每個使用者端是不同的。
我們來測試一下
順序是:
m,a,s,p
順序是:
m,p,a,s
順序是:
m,p,s,a
順序是:
m,a,s,p
這個看似很小的差異,也可以用於指紋識別
它用於與TLS 指紋識別類似的目的:比如反 DDOS 和反指令碼等自動爬蟲(提高門檻),只允許真實瀏覽器等。
ja3是tls指紋的標準,wiresharp也預設帶有
搞http2指紋的目前市面上還沒有標準,
我開源了一款提取tls&http2指紋的中介軟體(面向aspnetcore的)
https://github.com/yuzd/ja3-csharp
線上測試:
{
"tlsVersion": "Tls12",
"tcpConnectionId": "0HMKCUARI97OU",
"tlsHashOrigin": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,29-23-24,0",
"tlsHashMd5": "cd08e31494f9531f560d64c695473da9",
"cipherList": [
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA"
],
"extentions": [
"server_name",
"extended_master_secret",
"renegotiation_info",
"supported_groups",
"ec_point_formats",
"session_ticket",
"application_layer_protocol_negotiation",
"status_request",
"signature_algorithms",
"signed_certificate_timestamp",
"key_share",
"psk_key_exchange_modes",
"supported_versions",
"compress_certificate",
"extensionApplicationSettings",
"padding"
],
"supportedgroups": [
"X25519",
"CurveP256",
"CurveP384"
],
"ecPointFormats": [
"uncompressed"
],
"proto": "HTTP/2",
"h2": {
"SETTINGS": {
"1": "65536",
"3": "1000",
"4": "6291456",
"6": "262144"
},
"WINDOW_UPDATE": "15663105"
}
}
知道了原理,還不好過嗎
指紋識別在整個網路中變得非常普遍,Http2的指紋相對來說不為人知,但是並不新鮮
比如這篇論文:
詳細介紹了一項具有類似結論的研究
本文參考了@lwthiker大神的研究,加上了自己的實踐(解析http2協定),感謝他的指點