Go net/http TLS Parmak İzi Düzeltme: uTLS ile Chrome Benzeri ClientHello

Go'nun standart net/http kütüphanesi statik bir ClientHello yayınladığı için Cloudflare ve Akamai tarafından anında engellenir. Bu rehberde uTLS, JA3/JA4 spoofing ve ProxyHat residential proxy entegrasyonunu adım adım anlatıyoruz.

Fixing Go's net/http TLS Fingerprint: Pass WAF Detection with uTLS
Bu makalede

Go ile bir web scraper veya otomasyon istemcisi yazdınız; kodunuz doğru, header'lar gerçekçi, rotasyon mantığı sağlam — ama yine de Cloudflare 403 döndürüyor. Sorun uygulama katmanında değil, TLS el sıkışmasında. Go net/http TLS parmak izi düzeltme, tam da bu noktada devreye girer: Go'nun crypto/tls paketi statik ve Chrome'a hiç benzemeyen bir ClientHello üretir, bu yüzden modern WAF'ler isteğinizi daha ilk pakette işaretler. Bu yazıda, parmak izini yakalamayı, Chrome ile karşılaştırmayı, refraction-networking/utls ile nasıl düzeltileceğini ve ProxyHat residential proxy'leri üzerinden nasıl temiz bir ağ kimliğiyle birleştirileceğini anlatacağız.

Go net/http TLS Parmak İzi Neden Engellenir?

Go'nun standart net/http kütüphanesi, crypto/tls üzerinden TLS el sıkışması yapar. Bu el sıkışma statiktir: şifre sıralaması derlenmiş sabitlerden gelir, GREASE değerleri içermez ve Chrome'un gönderdiği birçok TLS extension'ı eksiktir. Sonuç, JA3/JA4 algoritmalarıyla özetlendiğinde çok belirgin bir parmak izidir.

Bu parmak izi neden sorun yaratır? Çünkü Cloudflare, Akamai, Imperva ve DataDome gibi WAF sağlayıcıları, gelen ClientHello paketini inceleyip JA3/JA4 özetini bilinen bot parmak izleriyle karşılaştırır. Go'nun ürettiği özet, gerçek bir tarayıcıdan o kadar farklıdır ki, tek paket bile bot tespiti için yeterlidir. Salesforce JA3 projesi, bu özetlerin nasıl hesaplandığını açıkça belgeler; TLS sürümü, cipher suite'leri, extension'lar ve eliptik eğri grupları virgülle birleştirilip MD5 ile özetlenir.

Pratikte, Go ile yapılan bir istek şu sinyalleri taşır:

  • Sabit cipher sıralaması: Chrome, ECDHE-ECDSA-AES128-GCM-SHA256'i öne koyar; Go farklı bir sıra kullanır.
  • GREASE yok: Chrome, cipher listesine ve extension listesine rastgele 0x0a0a benzeri yer tutucu değerler ekler; Go hiç eklemez.
  • Eksik extension'lar: application_settings, encrypted_client_hello, delegated_credentials gibi Chrome'un gönderdiği alanlar Go'da yok.
  • supported_groups ve key_share farkları: Chrome X25519MLKEM768 ile post-quantum anahtar paylaşımı yapar; Go bu eğriyi göndermez.

Tek paket, tek karar. WAF'ler için bu, IP itibarını dahi kontrol etmeden bot sinyali yeter.

Kendi JA3/JA4 Parmak İzinizi Yakalayın

Teoriyi kanıtlamak için kendi parmak izinizi görmelisiniz. tls.peet.ws/api/all endpoint'i, gelen ClientHello'yu ayrıştırıp JA3, JA4 ve JA4H özetlerini döndürür. Go ile basit bir istek atın:

package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    resp, err := http.Get("https://tls.peet.ws/api/all")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Çıktıya bakın. ja3 alanında Go'ya özgü bir özet, ja4 alanında ise t13d1516h2_ önekli bir dize göreceksiniz — ama Chrome'un t13d1515h2_… deseninden belirgin biçimde farklı. İşte bu fark, WAF'lerin sizi yakaladığı yerdir.

Karşılaştırma için aynı URL'yi gerçek bir Chrome sekmesinden açın. tls.peet.ws size Chrome'un JA3/JA4 özetini verecektir. Go özetini Chrome özetinin yanına koyun: cipher listesi, extension listesi ve supported_groups dizilimi tamamen farklıdır.

ClientHello Sinyalleri: Go vs Chrome Karşılaştırması

Şimdi sinyalleri somut cipher değerleriyle inceleyelim. Aşağıdaki tablo, Go 1.22'nin standart crypto/tls ClientHello'su ile Chrome 120+ arasındaki temel farkları özetler.

SinyalGo net/httpChrome 120+
Cipher suite #1TLS_AES_128_GCM_SHA256 (0x1301)GREASE (0x0a0a) → TLS_AES_128_GCM_SHA256
GREASE değerleriYokCipher, extension, supported_groups ve ALPN listelerinde
supported_groupsx25519, secp256r1, secp384r1GREASE, x25519MLKEM768, x25519, secp256r1, secp384r1
signature_algorithmsPKCS1WithSHA256, ECDSAWithP256AndSHA256, …Chrome'un sıralaması: ecdsa_secp256r1_sha256, rsa_pss_rsae_sha256, …
ALPNh2, http/1.1GREASE, h2, http/1.1
key_sharex25519, secp256r1GREASE, x25519MLKEM768, x25519
application_settingsYokVar (ALPS üzerinden)
encrypted_client_helloYokVar (ECH destekli)

Her satır, WAF için ayrı bir tespit sinyalidir. Örneğin GREASE değerlerinin olmaması tek başına bot göstergesidir; çünkü 2018'den beri tüm büyük tarayıcılar GREASE gönderir. RFC 8701, GREASE mekanizmasını resmi olarak tanımlar ve tarayıcıların bu değerleri göndermesi beklendiğini belirtir.

Pratik ipucu: WAF'ler yalnızca tek bir sinyale bakmaz; bir skor hesaplar. GREASE eksikliği 3 puan, supported_groups farkı 4 puan, application_settings yokluğu 2 puan ekleyebilir. Eşik aşıldığında istek engellenir.

Çözüm: refraction-networking/utls ile Chrome Parmak İzi Taklidi

refraction-networking/utls, Go'nun TLS el sıkışmasını değiştirmenin standart yoludur. Bu kütüphane, crypto/tls'in Conn tipini genişletir ve önceden tanımlanmış tarayıcı parmak izleriyle ClientHello üretir. uTLS HelloChrome_Auto, en yaygın kullanılan preset'tir ve Chrome'un güncel parmak izini taklit eder.

Kurulum:

go get github.com/refraction-networking/utls

Temel kullanım — bir utls.UClient oluşturup HelloChrome_Auto parmak izini uygulayın:

package main

import (
    "context"
    "net"
    "net/http"
    "time"

    utls "github.com/refraction-networking/utls"
)

func newUTLSTransport() *http.Transport {
    return &http.Transport{
        DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            host, port, _ := net.SplitHostPort(addr)
            rawConn, err := (&net.Dialer{}).DialContext(ctx, network, addr)
            if err != nil {
                return nil, err
            }
            cfg := &utls.Config{
                ServerName: host,
                NextProtos:  []string{"h2", "http/1.1"},
            }
            uConn := utls.UClient(rawConn, cfg, utls.HelloChrome_Auto)
            if err := uConn.HandshakeContext(ctx); err != nil {
                rawConn.Close()
                return nil, err
            }
            _ = port
            return uConn, nil
        },
        ForceAttemptHTTP2:     true,
        MaxIdleConns:           100,
        IdleConnTimeout:        90 * time.Second,
        TLSHandshakeTimeout:    10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
}

func main() {
    client := &http.Client{Transport: newUTLSTransport(), Timeout: 30 * time.Second}
    resp, err := client.Get("https://tls.peet.ws/api/all")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    // Artık JA3/JA4 Chrome ile eşleşiyor
    body := make([]byte, 4096)
    n, _ := resp.Body.Read(body)
    println(string(body[:n]))
}

Bu örnekte DialTLSContext, standart TCP bağlantısını açar ve üzerine utls.UClient ile Chrome parmak izini uygular. HelloChrome_Auto, en güncel Chrome parmak izini otomatik seçer; Safari gerekiyorsa HelloSafari_16_0 kullanın. NextProtos alanında ALPN değerlerini açıkça belirtmek, HTTP/2 müzakeresinin doğru yapılmasını sağlar.

Dikkat: HelloChrome_Auto zaman içinde güncellenir. Chrome yeni bir TLS extension eklediğinde, uTLS yeni bir preset yayınlar. Bu yüzden go get -u github.com/refraction-networking/utls ile periyodik güncelleyin.

HTTP/2 Üzerinde Ekstra Uyum

uTLS yalnızca TLS katmanını düzeltir. HTTP/2 frame sıralaması ve SETTINGS parametreleri ayrı bir parmak izi alanıdır (JA4H bunu yakalar). Go'nun golang.org/x/net/http2 paketi, Chrome'dan farklı SETTINGS değerleri gönderir: HEADER_TABLE_SIZE 4096 yerine 65536, INITIAL_WINDOW_SIZE farklı. Bu farkları da hizalamak için github.com/refraction-networking/utls/dicttls veya özel HTTP/2 transport katmanı kullanmanız gerekebilir. Tam uyum için CycleTLS veya azuretls-client gibi üst seviye kütüphaneler bu hizalamayı dahili yapar.

Üst Seviye Alternatifler: CycleTLS ve azuretls-client

Her seferinde DialTLSContext yazmak istemiyorsanız, iki olgun alternatif vardır.

CycleTLS

CycleTLS, Go ve Node.js için TLS parmak izi taklidi yapan bir kütüphanedir. uTLS'i sarmalar ve Ja3 string'i ile User-Agent'ı birlikte ayarlamanıza izin verir. Basit API:

import "github.com/Danny-Dasilva/CycleTLS/cycletls"

cycle := cycletls.Init()
resp, err := cycle.Do("https://tls.peet.ws/api/all", cycletls.Options{
    Ja3: "771,4865-4866-4867-49195-49199-...,0-23-65257-...,29-23-24,0",
    UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
}, "GET")

CycleTLS, JA3 string'ini manuel vermenizi ister; bu, esnektir ama güncel Chrome parmak izini kendiniz takip etmeniz demektir.

azuretls-client

azuretls-client, daha yüksek seviyeli bir HTTP istemcisidir. uTLS'i dahili kullanır, HTTP/2 frame sıralamasını Chrome ile hizalar ve proxy desteği yerleşiktir:

import "github.com/Noooste/azuretls-client"

session := azuretls.NewSession()
session.Browser = azuretls.Chrome
session.SetProxy("http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080")
resp, err := session.Get("https://tls.peet.ws/api/all")

azuretls-client, TLS + HTTP/2 + header sıralamasını tek seferde hizalar. Proxy entegrasyonu da tek satır.

ÖzellikManuel uTLSCycleTLSazuretls-client
TLS parmak izi kontrolüTamManuel JA3 stringOtomatik (Chrome/Safari/Firefox)
HTTP/2 frame uyumuYok (ekstra gerekir)KısmiTam
Proxy desteğiManuelVarYerleşik
Bakım sıklığıYüksekOrtaDüşük
Öğrenme eğrisiDikOrtaAlçak

Parmak İzi Taklidi Tek Başına Yetmez: ProxyHat Residential Proxy ile Temiz Ağ Kimliği

uTLS ile JA3'ünüz Chrome ile eşleşti — ama istek hâlâ engelleniyorsa, sorun IP katmanındadır. WAF'ler JA3/JA4'ü IP itibarıyla birlikte değerlendirir. Bir veri merkezi IP'sinden gelen Chrome benzeri ClientHello, yine de şüpheli görünür; çünkü gerçek Chrome kullanıcıları veri merkezi bloklarından gezinmez.

Çözüm: uTLS istemcisini ProxyHat residential proxy'leri üzerinden yönlendirmek. Böylece JA3 ve IP itibarı aynı anda insan benzeri görünür.

Go ile ProxyHat Üzerinden uTLS İsteği

Aşağıdaki örnek, uTLS el sıkışmasını ProxyHat residential proxy'si üzerinden yapar. Proxy HTTP CONNECT tüneli kullanır; TLS el sıkışması proxy üzerinden gerçek hedefe yapılır, bu yüzden JA3 hedef sunucu tarafından doğru biçimde görülür.

package main

import (
    "context"
    "fmt"
    "io"
    "net"
    "net/http"
    "net/url"
    "time"

    utls "github.com/refraction-networking/utls"
)

func newProxiedUTLSTransport(proxyURL string) *http.Transport {
    proxy, _ := url.Parse(proxyURL)
    return &http.Transport{
        Proxy: http.ProxyURL(proxy),
        DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            // 1. Proxy'ye bağlan
            proxyAddr := proxy.Host
            rawConn, err := (&net.Dialer{Timeout: 15 * time.Second}).DialContext(ctx, "tcp", proxyAddr)
            if err != nil {
                return nil, err
            }
            // 2. CONNECT tüneli aç
            host, _, _ := net.SplitHostPort(addr)
            connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\nProxy-Authorization: %s\r\n\r\n",
                addr, addr, basicAuth(proxy.Username, "pass"))
            _, err = rawConn.Write([]byte(connectReq))
            if err != nil {
                rawConn.Close()
                return nil, err
            }
            // CONNECT yanıtını oku (200 OK beklenir)
            buf := make([]byte, 1024)
            n, _ := rawConn.Read(buf)
            if !bytes.Contains(buf[:n], []byte("200")) {
                rawConn.Close()
                return nil, fmt.Errorf("CONNECT failed: %s", buf[:n])
            }
            // 3. Tünel üzerinden uTLS el sıkışması
            cfg := &utls.Config{ServerName: host, NextProtos: []string{"h2", "http/1.1"}}
            uConn := utls.UClient(rawConn, cfg, utls.HelloChrome_Auto)
            if err := uConn.HandshakeContext(ctx); err != nil {
                rawConn.Close()
                return nil, err
            }
            return uConn, nil
        },
        ForceAttemptHTTP2: true,
    }
}

func basicAuth(user, pass string) string {
    return "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+pass))
}

func main() {
    // ProxyHat residential proxy — US çıkışlı, sticky session
    proxyURL := "http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080"
    client := &http.Client{
        Transport: newProxiedUTLSTransport(proxyURL),
        Timeout:   30 * time.Second,
    }
    resp, err := client.Get("https://tls.peet.ws/api/all")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Bu örnekte dikkat edin: user-country-US-session-abc123 kullanıcı adı formatı, ProxyHat'ın geo-hedefleme ve sticky session özelliklerini kullanır. gate.proxyhat.com:8080 HTTP proxy gateway'idir. CONNECT tüneli sayesinde TLS el sıkışması proxy tarafından değil, hedef sunucu tarafından görülür; yani JA3/JA4 doğru biçimde iletilir.

SOCKS5 gerekiyorsa socks5://user-country-US-session-abc123:pass@gate.proxyhat.com:1080 formatını kullanın. SOCKS5, CONNECT'ten daha az ek yük getirir ve bazı senaryolarda 10–15 ms daha düşük gecikme sağlar.

Önemli: uTLS parmak izini değiştirseniz bile, veri merkezi IP'si kullanıyorsanız WAF'ler yine de bot skorunuzu yükseltir. Residential proxy + uTLS kombinasyonu, en düşük engellenme oranını verir. ProxyHat'ın lokasyon sayfasında 190+ ülke seçeneği bulunur; hedef kitlenizin bulunduğu ülkeyi seçerek coğrafi tutarlılığı da sağlayın.

JA4, JA3'ün Yerini Alıyor: Parrot'ları Güncel Tutun

JA4, FoxIO JA4 projesi tarafından geliştirilen daha keskin bir parmak izi standardıdır. JA3'ten farkı: cipher listesini sıralamaz (alfabetik sıralar), bu yüzden tarayıcıların küçük cipher sıralaması değişiklikleri JA4'ü bozmaz. Ayrıca TLS sürümü, SNI varlığı, ALPN ve extension sayısını ayrı segmentler olarak kodlar. Sonuç: t13d1516h2_… formatında bir özet. Go'nun JA4'ü Chrome'dan farklıdır çünkü extension sayısı ve supported_groups farklıdır.

uTLS'in HelloChrome_Auto preset'i, JA3'ü iyi taklit eder ama JA4'ü her zaman %100 eşlemez; özellikle Chrome yeni extension eklediğinde. Bu yüzden:

  1. Periyodik doğrulama: Her ay tls.peet.ws/api/all üzerinden hem JA3 hem JA4 çıktınızı gerçek Chrome ile karşılaştırın.
  2. uTLS güncelleme: go get -u github.com/refraction-networking/utls@latest ile yeni preset'leri alın.
  3. Parrot sürümü takibi: Chrome major sürüm değiştirdiğinde JA4 değişir. uTLS genelde 2–4 hafta içinde yeni parrot yayınlar.
  4. HTTP/2 frame parmak izi: JA4H, HTTP/2 SETTINGS frame'ini de özetler. Go'nun standart http2 transport'u Chrome'dan farklı SETTINGS gönderir; azuretls-client bu farkı kapatır.

Bu adımları takip eden ekipler, genelde %95+ başarı oranına ulaşır. uTLS + residential proxy kombinasyonu ile SERP scraping, fiyat izleme ve sosyal medya araştırması senaryolarında engellenme oranı belirgin biçimde düşer. Daha fazla scraping senaryosu için web scraping kullanım sayfamızı inceleyin; SERP takibi için SERP tracking rehberimize bakın. Fiyatlandırma detayları için ProxyHat fiyatlandırma sayfasını ziyaret edin; resmi proxy bağlantı parametreleri için ProxyHat dokümantasyonuna başvurun.

Yasal ve Etik Çerçeve

TLS parmak izi düzeltme, yalnızca yetkisiz erişim için bir teknik değildir. Meşru kullanım alanları şunlardır:

  • Güvenlik araştırması: WAF davranışını anlamak, kendi siteniz üzerinde sızma testi yapmak.
  • Meşru otomasyon: Hedef sitenin ToS'una ve robots.txt kurallarına uyarak veri toplama.
  • Fiyat izleme: Rakip fiyatlarını toplamak için公开发 API'lerine erişim.
  • QA testi: Kendi uygulamanızın farklı TLS istemcilerinden gelen istekleri nasıl işlediğini doğrulama.

Her durumda, hedef sitenin kullanım koşullarına ve robots.txt dosyasına saygı gösterin. GDPR ve CCPA kapsamında kişisel veri toplamıyorsanız bu teknikler sorun yaratmaz; kişisel veri içeriyorsa açık rıza gerekir.

Key Takeaways

  • Go'nun crypto/tls ClientHello'su statiktir; GREASE eksiktir, cipher sıralaması Chrome'dan farklıdır ve eksik extension'lar içerir — bu yüzden JA3/JA4 belirgin biçimde farklıdır.
  • refraction-networking/utls ile HelloChrome_Auto kullanarak TLS el sıkışmasını Chrome ile hizalayın; Safari için HelloSafari_16_0 kullanın.
  • uTLS tek başına yetmez; HTTP/2 frame sıralaması (JA4H) ayrı bir parmak izi alanıdır. Tam uyum için azuretls-client veya CycleTLS kullanın.
  • Parmak izi + IP itibarı birlikte değerlendirilir; uTLS istemcisini ProxyHat residential proxy üzerinden gate.proxyhat.com:8080 ile yönlendirin.
  • JA4, JA3'ün yerini alıyor; parrot'ları her ay doğrulayın ve uTLS'i güncel tutun.
  • Meşru otomasyon ve güvenlik araştırması için kullanın; hedef sitenin ToS ve robots.txt kurallarına uyun.

Go ile inşa ettiğiniz scraper'lar TLS parmak izi yüzünden engelleniyorsa, uTLS + ProxyHat residential proxy kombinasyonu en düşük engellenme oranını sunar. ProxyHat fiyatlandırmasını inceleyin ve ilk isteğinizi bugün gönderin.

Sık sorulan sorular

Go net/http TLS parmak izi düzeltme nedir?

Go net/http TLS parmak izi düzeltme, Go'nun crypto/tls paketinin ürettiği statik ve Chrome'a benzemeyen ClientHello paketini refraction-networking/utls kütüphanesi ile Chrome benzeri bir parmak iziyle değiştirme işlemidir. Go varsayılan olarak GREASE değerleri göndermez, cipher sıralaması farklıdır ve birçok Chrome extension'ı eksiktir; bu yüzden JA3/JA4 özeti WAF'ler tarafından anında bot olarak işaretlenir. uTLS HelloChrome_Auto preset'i bu farkları kapatır.

Go TLS parmak izi neden Chrome'dan farklı?

Go'nun crypto/tls paketi derleme zamanında sabitlenen bir cipher suite sıralaması kullanır, GREASE değerleri (RFC 8701) içermez ve application_settings, encrypted_client_hello, x25519MLKEM768 gibi Chrome'un gönderdiği extension ve supported_groups değerlerini eksiktir. Chrome ise her listede rastgele GREASE yer tutucuları ekler ve post-quantum anahtar paylaşımı yapar. Bu farklar JA3 ve JA4 özetlerinde belirgin biçimde görünür ve WAF'ler tarafından bot sinyali olarak değerlendirilir.

TLS parmak izi düzeltme için hangi proxy tipi en iyisidir?

Residential proxy en iyi seçenektir. uTLS ile JA3/JA4'ü Chrome ile hizalasanız bile, veri merkezi IP'si kullanıyorsanız WAF'ler IP itibarını da değerlendirdiği için istek yine şüpheli görünür. Residential proxy'ler gerçek ISP atanmış IP'leri kullandığı için hem JA3 hem IP itibarı aynı anda insan benzeri görünür. ProxyHat residential proxy'leri gate.proxyhat.com:8080 HTTP gateway'i üzerinden ülke ve şehir bazlı hedefleme ile sticky session desteği sunar.

uTLS ile engellenmekten nasıl kaçınılır?

Üç katmanı birlikte hizalamalısınız: uTLS HelloChrome_Auto ile TLS ClientHello'yu Chrome ile eşleştirin, HTTP/2 SETTINGS frame sıralamasını JA4H uyumlu yapmak için azuretls-client veya CycleTLS kullanın ve tüm trafiği ProxyHat residential proxy üzerinden gate.proxyhat.com:8080 adresine yönlendirin. Ayrıca her ay tls.peet.ws/api/all üzerinden JA3/JA4 çıktınızı doğrulayın ve uTLS kütüphanesini güncel tutun.

Başlamaya hazır mısınız?

148'den fazla ülkede residential, ISP ve mobil proxy'ler. Ücretsiz hesap oluşturun.

Ücretsiz hesap oluştur
← Bloga Dön