突破神奇的Cloudflare防火墻的網絡安全學習

背景

最近碰到一個神奇的網站,在瀏覽器可以打開,但是通過 curl 或者 代碼訪問就直接 403,我估摸著這肯定是做瞭UA校驗,於是請求的時候把瀏覽器的 UA 給帶上,然後訪問發現還是 403,不過這也難不倒我,肯定是還有校驗其它的請求頭,直接瀏覽器打開 network,把所有的請求頭復制過來並且帶上,確保我和瀏覽器在 http 協議層面的請求完全一樣,這樣不可能會失敗瞭吧,然而運行完發現還是 403。

放個地址: https://pixabay.com

思考

服務端校驗客戶端沒有什麼黑魔法,因為都是通過 TCP 協議通訊,不可能存在瀏覽器發送一個 HTTP 報文和我發送一個同樣的 HTTP 報文服務器能識別出來,既然不是校驗的 HTTP 層,那隻可能是在 TLS 層校驗的,於是祭出wireshark抓包,看看能不能找到 TLS 握手中差異化的東西,眾所周知在 TLS 握手時有一個客戶端發送給服務端的Client Hello報文,很有可能就是根據它來辨別瀏覽器和非瀏覽器請求的,因為在這個報文中,客戶端要告訴服務端支持的加密套件,TLS 版本等等信息,而這些信息根據客戶端的實現都會有所差異,先抓個正常瀏覽器請求的報文看看,如下圖:

然後再通過 curl 訪問抓包,如下圖;

可以看到兩邊的報文確實存在很大的差異,逐一對比排查之後發現很有可能是因為 curl 的請求報文裡缺少supported_versions擴展信息導致的 403,瀏覽器那邊在此擴展信息內容如圖:

表示支持TLSv1.2TLSv1.3,而且最終握手之後的協議也是切換到瞭TLSv1.3,在上面兩個對比圖可以看到,瀏覽器走的是TLSv1.3,而 curl 走的是TLSv1.2,可能是一定要使用TLSv1.3才能訪問成功。

驗證

馬上 google 瞭下如何指定 curl 的 TLS 版本,發現隻需要加上--tlsv1.3參數就可以瞭,如下:

$ curl -I --tlsv1.3 'https://pixabay.com/'  \
> -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
> -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.49'
HTTP/2 200
date: Fri, 22 Jul 2022 02:40:35 GMT
content-type: text/html; charset=utf-8
cf-ray: 72e8cffc18c73d5a-HKG
cache-control: s-maxage=86400
content-language: en
vary: Accept-Encoding, Cookie, Accept-Language
cf-cache-status: MISS
content-security-policy: frame-ancestors none
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
referrer-policy: strict-origin-when-cross-origin
x-frame-options: DENY
set-cookie: __cf_bm=Cy4a751rDND6kHhu.RzEr5DpqnaxRdpUxaMfNfkya0A-1658457635-0-AS1DaewDqNjWHZ/m74A88bNyEG0EFsZAwmsm/ON5QQEuh8B6XOS7PkSnhGgXPLV+LtEvzOKTy/WWHmwY63uGlD0=; path=/; expires=Fri, 22-Jul-22 03:10:35 GMT; domain=.pixabay.com; HttpOnly; Secure; SameSite=None
server: cloudflare
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400

經過反復驗證,發現除瞭要指定tlsv1.3之外,還需要加上accept-languageuser-agent頭,並且一定得是 http2 協議,三個條件缺一不可。

nodejs 訪問

上面說到瞭一定要走 http2 協議,而現在市面上流行的 http client 基本都是隻支持 http1.1,所以隻能直接從基礎庫入手瞭,官方有一個http2的庫,一番調教之後也是成功請求瞭,代碼如下:

const http2 = require("http2");
function get(host, path) {
  return new Promise((resolve, reject) => {
    const session = http2.connect(`https://${host}`, {
      minVersion: "TLSv1.3",
      maxVersion: "TLSv1.3",
    });
    session.on("error", (err) => {
      reject(err);
    });
    const req = session.request({
      [http2.constants.HTTP2_HEADER_AUTHORITY]: host,
      [http2.constants.HTTP2_HEADER_METHOD]: http2.constants.HTTP2_METHOD_GET,
      [http2.constants.HTTP2_HEADER_PATH]: path,
      "user-agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50",
    });
    req.setEncoding("utf8");
    let data = "";
    req.on("data", (chunk) => {
      data += chunk;
    });
    req.on("end", () => {
      session.close();
      if (data) {
        try {
          resolve(data);
        } catch (e) {
          reject(e);
        }
      }
    });
    req.on("error", (err) => {
      reject(err);
    });
    req.end();
  });
}
(async function () {
  const data = await get("pixabay.com", "/");
  console.log(data);
})();

深入

雖然已經成功請求瞭,但是本著探索的精神繼續深入發現 cloudflare 官方有一篇博客就是專門介紹這個 TLS 攔截技術的,鏈接如下:
https://blog.cloudflare.com/monsters-in-the-middleboxes/

其中有一段內容也證明瞭我的猜想,翻譯後如下:

也就是說 cloudflare 會維護一組瀏覽器的 TLS 指紋,當收到一個 Client Hello 請求時,會檢查這組指紋,如果匹配不上,就會攔截這個請求,這樣可以攔截掉大部分不是來自瀏覽器的請求瞭。

以上就是突破神奇的Cloudflare防火墻的網絡安全學習的詳細內容,更多關於Cloudflare防火墻的資料請關註WalkonNet其它相關文章!

推薦閱讀: