HTTP2指紋識別(一種相對不爲人知的網絡指紋識別方法)

image
這是關於網絡指紋識別的兩部分系列的第二部分

上一部分我介紹了有關TLS 指紋識別方法(以及在不同客戶端的指紋有何區別):

https://mp.weixin.qq.com/s/BvotXrFXwYvGWpqHKoj3uQ

HTTP/2 指紋識別

和Tls指紋類似也是一種 Web 服務器可以依賴指紋來識別哪個客戶端。

例如,它可以識別瀏覽器類型和版本,或者是否使用了腳本(你是真實瀏覽器啊還是ScriptBoy?)。

該方法依賴於 HTTP/2 協議的內部結構,與其更簡單的前身 HTTP/1.1 相比,這些內部結構鮮爲人知。

在這篇文章中,我將首先簡要介紹 HTTP/2協議,然後詳細介紹我們可以協議的哪些參數來識別你究竟誰(what are you)!

與HTTP/1.1相比

使用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/2 是 HTTP 協議的主要修訂版,從 2015 年左右開始出現。現在大約一半的網站使用 HTTP/2

image

基本上所有流行的網站都默認使用它!

如何看服務端使用的是否是http2協議呢?

在chrome上看是這樣的

image

在Firefox上看是這樣的
image

HTTP/2 的主要目標是提高性能
  • 多路複用(Multiplexing ) - 多個請求和響應可以同時共享同一個 TCP 連接,從而減少了獲取具有大量資源(圖像、腳本等)的站點的時間。
  • 優先級(PRIORITY) - HTTP/2 支持對某些請求和響應進行優先級排序。
  • 服務器推送(Server push) - 在 HTTP/2 中,服務器可以在客戶端請求資源之前將資源發送給客戶端。

然而,HTTP 協議的應用程序語義沒有改變:它仍然由熟悉的請求/響應模型組成,包括 URI、HTTP 方法、HTTP 標頭和狀態碼。

Frames and Streams

HTTP/2 是一種二進制協議,與文本 HTTP/1.1 不同。HTTP/2 中的消息由幀組成,有十種不同用途的幀。幀始終是流的一部分。
image

Stream都是有編號的,從0開始

如上圖:編號爲0的Stream包含如下

  • SETTINGS是客戶端發送的第一幀,包含 HTTP/2 的特定配置,
  • WINDOW_UPDATE- 增加接收器的窗口大小,下面會講到

然後是編號開始遞增,代表了客戶端給服務端發送的實際請求,如上圖爲1的Stream:

  • HEADERS 包含 URI、HTTP 方法和客戶端的 HTTP 頭
  • DATA 包含來請求的資源數據以及服務器的響應

使用 HTTP/2 進行客戶端指紋識別

研究http2協議的工具

這裏推薦使用nghttpd,它可以很方便的創建一個http2協議的webserver。
最關鍵的是,讓客戶端請求的時候它能夠直觀的把每一幀都給打印出來(下面會給大家演示)
我將它安裝在wsl的ubuntu機器上,還得自建一個證書,這裏我遇到了一點坑,
避坑指南請看我寫的(wsl創建證書讓chrome瀏覽器識別):

下面就是如何使用nghttpd跑h2協議server
image

我這裏分別使用如下客戶端來測試

  • Chrome瀏覽器
  • Firefox瀏覽器
  • CURL
  • Python腳本

1. SETTINGS

上面介紹到這是客戶端發送的第一幀,裏面有一些特殊配置

Chrome

image

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]

Firefox

image

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]

CURL

image

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]

PYTHON

image

image

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幀裏面配置,
不同的客戶端設置的種類和值都是不同的,這使得很容易區分是否是瀏覽器,
而且這個配置不容易控制,可以用於指紋識別!

WINDOW_UPDATE

HTTP/2 實現了一種流控制機制。
流量控制爲接收方提供了在每個流的基礎上調節流量的機制。
使用WINDOW_UPDATE大小來實現的

默認窗口大小由SETTINGS幀裏面的 SETTINGS_INITIAL_WINDOW_SIZE中的值控制,
參考上方測試,可以看到 Chrome 使用 6MB (6291456) 而 Firefox 使用 128KB (131072)

當客戶端接收數據時,它可以使用WINDOW_UPDATE框架來調整窗口大小,從而增加其窗口大小。

Chrome
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=15663105)

Chrome 實際上將連接級窗口大小增加到 15MB (15663105+65535=15MB)

Firefox
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=12517377)

Firefox 會將其增加到 12MB

CURL
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#L62

PYTHON
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=16777216)

PYTHON 會將其增加到 16MB

所以我們也可以使用該參數用於指紋識別!

HEADERS

這個有點意思了

從廣義上講,HEADERS 包含了 HTTP/1.1 的所有功能,包含了
URI、方法(GET/POST/等)和客戶端的頭等!

下面的幾個僞標頭的順序對於每個客戶端是不同的。

  • :method
  • :authority
  • :scheme
  • :path

我們來測試一下

Chrome

image

順序是:
m,a,s,p

Firefox

image

順序是:
m,p,a,s

CURL

image

順序是:
m,p,s,a

Python

image

順序是:
m,a,s,p

這個看似很小的差異,也可以用於指紋識別

HTTP/2 指紋識別在哪裏使用?

它用於與TLS 指紋識別類似的目的:比如反 DDOS 和反腳本等自動爬蟲(提高門檻),只允許真實瀏覽器等。

如何讓你的server具有提取客戶端HTTP2指紋的能力

ja3是tls指紋的標準,wiresharp也默認帶有

image

搞http2指紋的目前市面上還沒有標準,
我開源了一款提取tls&http2指紋的中間件(面向aspnetcore的)

https://github.com/yuzd/ja3-csharp

image

在線測試:

https://kawayiyi.com/tls


{
    "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指紋呢?

知道了原理,還不好過嗎

總結

指紋識別在整個網絡中變得非常普遍,Http2的指紋相對來說不爲人知,但是並不新鮮

比如這篇論文:

https://www.blackhat.com/docs/eu-17/materials/eu-17-Shuster-Passive-Fingerprinting-Of-HTTP2-Clients-wp.pdf

詳細介紹了一項具有類似結論的研究

本文參考了@lwthiker大神的研究,加上了自己的實踐(解析http2協議),感謝他的指點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章