TLS Parmak İzi ve JA3 Nedir, Nasıl Çalışır?

Yayın tarihi:

15 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
Okuyucu kapının gövdesindeki ClientHello alanlarından üstteki panele JA3 özeti çıkıyor, betik istemcisi kapıdan geçemiyor

Python ile yazdığınız bir betik var. Proxy üzerinden çıkıyor, IP adresi her istekte değişiyor, User-Agent başlığına güncel bir Chrome satırı yazdınız. Buna rağmen site daha ilk istekte 403 dönüyor. Aynı adresi aynı IP ile gerçek bir tarayıcıda açtığınızda ise sayfa geliyor. Aradaki fark, HTTP başlıklarından önce giden bir pakettedir: şifreli bağlantıyı başlatan ClientHello mesajı. Site bu mesajın düzenine bakarak karşısındakinin Chrome mu, Python mu, curl mü olduğunu başlıkları okumadan anlayabilir.

Bu yazıda TLS el sıkışmasını, ClientHello mesajındaki alanları, JA3 dizgisinin bu alanlardan nasıl hesaplandığını ve JA4'ün neyi farklı yaptığını anlatıyoruz. Ardından en çok sorulan soruya geçiyoruz: proxy TLS parmak izini değiştirir mi? Sonda kendi istemcinizin JA3 dizgisini yerelde gösteren, denenmiş bir Python örneği var.

TLS el sıkışması nedir?

TLS, tarayıcı ile site arasındaki trafiği şifreleyen protokoldür; adres çubuğundaki https:// onu kullandığınızı gösterir. Şifreleme başlamadan önce iki tarafın hangi sürümü, hangi şifre takımını ve hangi anahtarı kullanacağı konusunda anlaşması gerekir. Bu kısa pazarlığa el sıkışma (handshake) denir. Güncel sürüm olan TLS 1.3, RFC 8446 belgesinde tanımlıdır ve el sıkışma kabaca şöyle ilerler:

  1. İstemci sunucuya TCP ile bağlanır ve bir ClientHello mesajı gönderir. Mesajda desteklediği şifre takımları, uzantılar ve anahtar paylaşımı bulunur.
  2. Sunucu listeden birini seçer ve ServerHello ile yanıtlar. Bu noktadan sonra iki taraf ortak anahtarı türetebilir.
  3. Sunucu sertifikasını ve el sıkışmanın bütünlüğünü kanıtlayan Finished mesajını şifreli olarak gönderir.
  4. İstemci sertifikayı doğrular, kendi Finished mesajını yollar.
  5. İlk HTTP isteği (GET /, başlıklar, User-Agent) ancak bundan sonra, şifreli kanalın içinden gider.

Parmak izi açısından önemli olan birinci adımdır. ClientHello henüz ortak anahtar yokken gönderildiği için şifresizdir; sunucu, önündeki CDN ve yol üzerindeki her ağ cihazı onu başlıklardan, çerezlerden ve JavaScript'ten önce olduğu gibi okuyabilir.

ClientHello mesajında hangi alanlar var?

ClientHello, istemcinin "şunları konuşabiliyorum" dediği bir listedir. Parmak izinde kullanılan alanlar şunlardır:

AlanNe taşır?Neden ayırt edici?
legacy_versionEski sürüm alanı. TLS 1.3 istemcileri uyumluluk için burada hâlâ TLS 1.2 değerini (771) yazarTek başına az bilgi taşır; gerçek sürüm uzantıdadır
cipher_suitesDesteklenen şifre takımları, tercih sırasıylaHem liste hem sıra kütüphaneden kütüphaneye değişir
extensionsUzantıların tür numaraları: server_name (0), supported_groups (10), signature_algorithms (13), ALPN (16), supported_versions (43), key_share (51) ve diğerleriHangi uzantıların bulunduğu ve sırası yazılıma özgüdür
supported_groupsAnahtar değişiminde kullanılabilecek eğriler ve gruplar (29 = x25519, 23 = secp256r1)Yeni gruplar önce tarayıcılara gelir
ec_point_formatsEğri noktası biçimleriBazı kütüphaneler üç değer, bazıları tek değer gönderir
signature_algorithmsKabul edilen imza algoritmaları, sıraylaJA3 kullanmaz, JA4 kullanır
ALPNİstemcinin konuşmak istediği uygulama protokolü (h2, http/1.1)Tarayıcı h2 ister, basit istemciler çoğu zaman istemez

Bu listeleri uygulamanız değil, altındaki TLS kütüphanesi doldurur. Chrome BoringSSL, Firefox NSS kullanır. Python'un ssl modülü ve Node.js OpenSSL üzerine kuruludur. Windows ile gelen curl, işletim sisteminin kendi TLS yığını olan Schannel'i kullanır. Her kütüphanenin varsayılan şifre listesi, uzantı seti ve sıralaması farklıdır; istemcinin kimliği böylece el sıkışmanın ilk paketine sızar.

TLS parmak izi nedir?

TLS parmak izi, ClientHello mesajındaki bu listelerin kısa ve karşılaştırılabilir bir dizgiye indirgenmiş hâlidir. Üç özelliği onu diğer sinyallerden ayırır:

  • Pasiftir. Site istemciye hiçbir şey çalıştırtmaz, yalnızca gelen ilk paketi okur. JavaScript'i kapatmak ya da çerezleri silmek sonucu etkilemez.
  • Başlıklardan bağımsızdır. User-Agent bir metin satırıdır ve her HTTP kütüphanesinde tek satırla değişir. ClientHello ise kütüphanenin derlenmiş davranışından gelir.
  • Kişiyi değil yazılımı tanımlar. Aynı işletim sisteminde aynı Chrome sürümünü kullanan herkes aynı değeri üretir. Cihazı tek tek ayırt etmeyi hedefleyen JavaScript katmanını (canvas, font, WebGL) Tarayıcı Parmak İzi Nedir? yazımızda anlattık.

Yöntem bot tespiti için icat edilmedi. İlk kullanım alanı ağ güvenliğiydi: zararlı yazılım trafiği şifreler, ama TLS kütüphanesi ve ayarları çoğu zaman sabittir. İçeriği göremeyen güvenlik ekibi, aynı yazılım ailesini ClientHello düzeninden tanıyabilir.

JA3 nedir ve nasıl hesaplanır?

JA3, bu fikri standart bir biçime bağlayan ilk yaygın yöntemdir. 2017'de Salesforce'ta John Althouse, Jeff Atkinson ve Josh Atkins tarafından geliştirildi ve açık kaynak olarak yayımlandı. Hesap, Salesforce'un JA3 deposunda anlatıldığı gibi beş adımdan oluşur:

  1. ClientHello mesajından beş alan alınır: TLS sürümü, şifre takımları, uzantılar, eğriler (supported_groups) ve eğri noktası biçimleri.
  2. Her alandaki değerler onluk sayıya çevrilir ve mesajdaki sırasıyla - ile birleştirilir.
  3. Beş alan , ile birleştirilir. Bir alan boşsa yeri boş bırakılır.
  4. GREASE değerleri (aşağıda anlatıyoruz) listeye hiç alınmaz.
  5. Ortaya çıkan dizginin MD5 özeti alınır. 32 karakterlik bu özet JA3 parmak izidir.

Deponun kendi örneği şöyledir:

text
769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0
→ ada70206e40642a3e4461f35503241d5

Soldan okuyunca: 769 TLS 1.0 demektir; ardından on iki şifre takımı, üç uzantı, üç eğri ve tek bir nokta biçimi gelir. MD5 burada güvenlik için değil, uzun dizgiyi sabit boyda aranabilir bir anahtara çevirmek için kullanılır.

İki not: TLS 1.3 istemcileri sürüm alanına TLS 1.2 değerini yazdığı için güncel JA3 dizgilerinin neredeyse hepsi 771 ile başlar. Salesforce depoyu 1 Mayıs 2025'te arşivledi; Wireshark gibi araçlar değeri hesaplamayı sürdürüyor, ama yöntem artık bakım görmüyor.

GREASE nedir, JA3 onu neden yok sayar?

GREASE, RFC 8701 ile tanımlanan bir dayanıklılık mekanizmasıdır. İstemci; şifre takımı, uzantı ve grup listelerine 0x0A0A, 0x1A1A0xFAFA kalıbındaki ayrılmış değerlerden rastgele birkaçını ekler. Bu değerlerin hiçbir anlamı yoktur. Amaç, sunucuların tanımadıkları değerleri sessizce yok sayıp saymadığını sürekli sınamaktır: tanımadığı değerde bağlantıyı kesen hatalı bir sunucu, yeni bir TLS özelliği çıktığında değil, bugün fark edilir.

Değerler rastgele seçildiği için JA3 bunları hesaba katsaydı aynı tarayıcı her bağlantıda farklı bir özet üretirdi. Yöntemin belgesi bu yüzden GREASE değerlerinin atlanmasını ister; aşağıdaki kodda bunu is_grease fonksiyonu yapıyor.

Chrome uzantı sırasını neden karıştırıyor?

GREASE listelerin içeriğini korur, sırasını korumaz. Sunucu ve ara cihaz yazılımlarının Chrome'un sabit uzantı sırasına güvenmesi aynı riski taşıyordu; Chrome ekibi sırayı da rastgele hâle getirdi. Chrome Platform Status'taki kayda göre "TLS ClientHello extension permutation" özelliği Chrome 110 ile varsayılan olarak açıldı. Kayıttaki gerekçe parmak izinden kaçmak değil, ekosistemin kırılganlaşmasını önlemektir: sabit bir sıra, sunucu geliştiricilerini Chrome'u tanıyıp belirli bir davranış varsaymaya iter ve ileride TLS'e yapılacak değişiklikleri zorlaştırır. RFC 8446 zaten uzantıların herhangi bir sırada gelebileceğini söyler; tek istisna, varsa en sonda durması gereken pre_shared_key uzantısıdır.

JA3 uzantıları mesajdaki sırasıyla birleştirdiği için bu değişiklik Chromium tabanlı tarayıcıların JA3 özetini kararsız kıldı. Aşağıdaki dinleyiciyle ölçtük: Chromium tabanlı bir tarayıcının art arda açtığı 24 bağlantıdan 24 farklı JA3 özeti çıktı. Uzantı numaralarını sıraladığımızda ise hepsinde aynı küme vardı; değişen yalnızca sıraydı.

JA4 nedir, JA3'ten farkı ne?

JA4, FoxIO'nun yayımladığı ve bu soruna cevap veren yeni nesil biçimdir. JA4 teknik belgesine göre parmak izi a_b_c biçiminde üç parçadan oluşur. Belgedeki örnek t13d1516h2_8daaf6152771_e5627efa2ab1 şöyle okunur:

  • t: TCP üzerinden TLS (q QUIC, d DTLS olurdu).
  • 13: TLS 1.3. JA4 sürümü eski alandan değil supported_versions uzantısından okur.
  • d: SNI uzantısı var, yani istemci bir alan adına bağlanıyor (i, SNI'ın bulunmadığını gösterir).
  • 15 ve 16: GREASE hariç 15 şifre takımı ve 16 uzantı.
  • h2: ALPN listesindeki ilk değerin ilk ve son karakteri, yani HTTP/2.
  • 8daaf6152771: şifre takımlarının onaltılık kodları sıralanır, SHA-256 özeti alınır, ilk 12 karakter tutulur.
  • e5627efa2ab1: uzantı kodları sıralanır (SNI ve ALPN hariç), sonuna imza algoritmaları özgün sırasıyla eklenir, aynı şekilde özetlenir.

Sıralama, uzantı karıştırmanın etkisini ortadan kaldırır. Okunabilir ilk parça ise özetlere bakmadan kaba bir ayrım yapmaya izin verir: biri h2, diğeri ALPN göndermediği için 00 ile biten iki ilk parça aynı yazılımdan gelmiyordur.

JA3JA4
YayımlayanSalesforce (2017), depo 2025'te arşivlendiFoxIO, geliştirme sürüyor
Biçim32 karakterlik tek MD5 özetia_b_c: okunabilir ön ek + iki kısaltılmış SHA-256
Uzantı sırasıMesajdaki sıraSıralanmış; karıştırmadan etkilenmez
TLS sürümüEski sürüm alanı (TLS 1.3'te de 771)supported_versions uzantısı
ALPN, SNIYalnızca uzantı numarası olarak görünürİkisi de ilk parçada açıkça yazılır
İmza algoritmalarıKullanılmazÜçüncü parçaya girer
GREASEYok sayılırYok sayılır

FoxIO'nun deposundaki lisans notuna göre TLS istemci parmak izi olan JA4, BSD 3-Clause lisansıyla açıktır; ailenin diğer üyeleri (JA4S, JA4H, JA4X, JA4T ve devamı) ayrı bir FoxIO lisansına bağlıdır.

Proxy TLS parmak izini değiştirir mi?

Hayır. Bunun nedeni proxy'nin HTTPS trafiğini nasıl taşıdığında yatar.

HTTP proxy'de istemci önce proxy'ye CONNECT hedef.com:443 isteği gönderir. Proxy hedefe bir TCP bağlantısı açar, 200 Connection Established der ve o andan sonra yalnızca bayt aktarır. ClientHello bu tünelin içinden geçer: onu sizin istemciniz üretir, hedef site okur, proxy ise ne içeriğini değiştirir ne de yeniden yazar. SOCKS5'te durum aynıdır; protokol TCP bağlantısını bir katman aşağıda aktarır ve taşıdığı verinin TLS olduğunu bilmez bile. İki protokolün adım adım akışını SOCKS ve HTTP Proxy Farkı yazımızda anlattık.

Sonuç şudur: site, proxy'nin IP adresini ve sizin istemcinizin TLS parmak izini aynı anda görür. HTTPS Proxy ya da SOCKS5 Proxy kullanmanız bunu değiştirmez; proxy'nin residential, mobil ya da datacenter olması da değiştirmez. Yerel bir test proxy'siyle denedik: aynı curl istemcisi aşağıdaki dinleyiciye doğrudan, HTTP CONNECT tüneliyle ve SOCKS5 üzerinden bağlandı, dinleyici üçünde de aynı JA3 dizgisini kaydetti.

Parmak izinin değiştiği tek durum, arada TLS'i sonlandıran bir aracının bulunmasıdır:

Aradaki şeyTLS'i hedefle kim kurar?Site hangi parmak izini görür?
HTTP proxy (CONNECT tüneli)Sizin istemcinizSizin istemcinizinkini
SOCKS5 proxySizin istemcinizSizin istemcinizinkini
VPNSizin istemcinizSizin istemcinizinkini
Kurumsal TLS denetimi yapan ağ geçidiAğ geçidiAğ geçidininkini
HTTPS taraması açık antivirüsAntivirüsAntivirüsünkünü
Sayfayı sizin adınıza çeken servisServisin kendi istemcisiServisin istemcisininkini

Beşinci satırı bu yazıyı hazırlarken kendi makinemizde yaşadık: Windows'taki curl, yankı servisine doğrudan bağlandığında başka, yerel proxy tünelinden geçtiğinde başka bir parmak izi döndü. Nedeni antivirüsün HTTPS taramasıydı; doğrudan bağlantıyı kendi TLS yığınıyla yeniden kuruyordu. Yankı servisindeki değer beklediğiniz kütüphaneye benzemiyorsa sertifikayı kimin imzaladığına bakın.

Kendi JA3 dizginizi nasıl görürsünüz?

En kısa yol bir yankı servisidir: tarayıcıda BrowserLeaks'in TLS sayfasını açtığınızda JA3 ve JA4 değerlerinizi görürsünüz. Paket düzeyinde bakmak isterseniz Wireshark ClientHello paketlerinde bu değerleri kendisi hesaplar; görüntüleme filtresi başvurusunda alanlar tls.handshake.ja3 ve tls.handshake.ja4 adlarıyla listelenir.

Hesabın kendisini görmek için aşağıdaki betiği kullanabilirsiniz. Yalnızca Python'un standart kütüphanesiyle çalışır ve dışarıya istek göndermez: 8443 portunda ham bir TCP dinleyicisi açar, bağlanan her istemcinin ClientHello mesajını ayrıştırıp JA3 dizgisini ve özetini yazdırır. Açılışta Python'un kendi urllib istemcisini bir kez bağlar. Dinleyici el sıkışmayı tamamlamaz, ilk paketi okuyup kapatır; istemci tarafındaki bağlantı hatası beklenen bir durumdur.

python
import hashlib
import socket
import threading
import urllib.request

HOST, PORT = "127.0.0.1", 8443


def is_grease(value):
    # RFC 8701: 0x0a0a, 0x1a1a, ... 0xfafa
    return (value & 0x0F0F) == 0x0A0A and (value >> 8) == (value & 0xFF)


def read_client_hello(conn):
    data = b""
    while len(data) < 5:
        data += conn.recv(4096)
    if data[0] != 22:  # 22 = handshake kaydı
        raise ValueError("TLS el sıkışması değil")
    record_length = int.from_bytes(data[3:5], "big")
    while len(data) < 5 + record_length:
        chunk = conn.recv(4096)
        if not chunk:
            break
        data += chunk
    return data[5 : 5 + record_length]


def ja3_from_client_hello(hello):
    if hello[0] != 1:  # 1 = ClientHello
        raise ValueError("ClientHello değil")
    pos = 4  # mesaj türü (1) + uzunluk (3)
    version = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2 + 32  # sürüm + random
    pos += 1 + hello[pos]  # session_id
    size = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2
    ciphers = [int.from_bytes(hello[i : i + 2], "big") for i in range(pos, pos + size, 2)]
    pos += size
    pos += 1 + hello[pos]  # compression_methods
    end = pos + 2 + int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2

    extensions, groups, point_formats = [], [], []
    while pos < end:
        ext_type = int.from_bytes(hello[pos : pos + 2], "big")
        ext_size = int.from_bytes(hello[pos + 2 : pos + 4], "big")
        body = hello[pos + 4 : pos + 4 + ext_size]
        pos += 4 + ext_size
        extensions.append(ext_type)
        if ext_type == 10:  # supported_groups
            groups = [int.from_bytes(body[i : i + 2], "big") for i in range(2, len(body), 2)]
        elif ext_type == 11:  # ec_point_formats
            point_formats = list(body[1:])

    def join(values):
        return "-".join(str(v) for v in values if not is_grease(v))

    fields = [str(version), join(ciphers), join(extensions), join(groups), join(point_formats)]
    ja3_text = ",".join(fields)
    return ja3_text, hashlib.md5(ja3_text.encode()).hexdigest()


def own_python_client():
    try:
        urllib.request.urlopen(f"https://localhost:{PORT}", timeout=5)
    except OSError:
        pass  # dinleyici yanıt vermeden kapatır, beklenen durum


def main():
    server = socket.create_server((HOST, PORT))
    print(f"https://localhost:{PORT} dinleniyor, çıkmak için Ctrl+C")
    threading.Thread(target=own_python_client, daemon=True).start()
    while True:
        conn, _ = server.accept()
        with conn:
            try:
                ja3_text, ja3_hash = ja3_from_client_hello(read_client_hello(conn))
            except (ValueError, IndexError, OSError) as exc:
                print("okunamadı:", exc)
                continue
        print(ja3_hash, ja3_text)


if __name__ == "__main__":
    main()

Betik çalışırken başka bir terminalden farklı istemcileri aynı adrese yönlendirin. Adreste localhost yazın; IP adresine bağlanan istemci SNI uzantısını göndermez ve parmak izi değişir.

bash
curl -k https://localhost:8443
node -e "require('https').get('https://localhost:8443', { rejectUnauthorized: false }).on('error', () => {})"

Bizim makinemizde (Windows 11, Python 3.13 ve OpenSSL 3.0, Node.js 24, Schannel ile derlenmiş curl 8) çıktı şöyleydi. Okunabilsin diye şifre takımı alanını kısalttık:

text
331a436afb23d4e31134c11b301bdcb5 771,4866-4867-4865-…,0-11-10-35-16-22-23-49-13-43-45-51-21,29-23-30-25-24-256-257-258-259-260,0-1-2
2e6c64f66822fc35b6a7a128b557f1de 771,4866-4865-49196-…,0-43-13-35-10-11-16-51-49-23-65281-45,29-23-24,0
944d1e1858cd278718f8a46b65d3212f 771,4866-4867-4865-…,65281-0-11-10-35-22-23-13-43-45-51,4588-29-23-30-24-25-256-257,0-1-2

Aynı makine, aynı IP, üç ayrı kimlik: sırasıyla Python, curl ve Node.js. Python'un özetini yankı servisi de birebir aynı verdi, yani ayrıştırıcı doğru çalışıyor. Node.js satırındaki 4588, IANA kayıtlarında X25519MLKEM768 adıyla geçen kuantum sonrası hibrit anahtar değişimi grubudur; Python'un bu sürümü onu göndermiyor. Aynı Node.js'te https.get ile yerleşik fetch de farklı JA3 verdi, çünkü fetch ALPN uzantısını ekliyor: parmak izi dile değil, kullanılan HTTP kütüphanesine bağlıdır. Python tarafındaki kütüphane farklarını HTTPX, Requests ve AIOHTTP yazımızda karşılaştırdık.

Proxy'nin etkisini ölçmek için dinleyici işe yaramaz (uzaktaki proxy sizin localhost adresinize ulaşamaz), bu yüzden yankı servisine iki kez sorun:

python
import json
import urllib.request

PROXY = "http://user:pass@pr.proxynet.io:8000"
URL = "https://tls.browserleaks.com/json"


def fingerprint(opener):
    with opener.open(URL, timeout=15) as response:
        result = json.load(response)
    return result["ja3_hash"], result["ja4"]


direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
proxied = urllib.request.build_opener(urllib.request.ProxyHandler({"https": PROXY}))

print("direct :", *fingerprint(direct))
print("proxied:", *fingerprint(proxied))

İki satırda aynı değerleri görürsünüz; değişen yalnızca servisin gördüğü IP adresidir.

Site sahibi bu veriyi nasıl kullanır?

CDN ve güvenlik duvarı ürünleri JA3 ya da JA4 değerini her isteğin yanında bir alan olarak sunar. Site sahibi bu alanı kural yazarken ve günlükleri incelerken kullanır. Tipik kullanımlar:

  • Tutarlılık kontrolü. User-Agent "Chrome" diyor ama parmak izi bilinen hiçbir Chrome sürümüne uymuyorsa, başlık değiştirilmiş demektir. Tek başına kesin kanıt değildir, ama puanı etkileyen güçlü bir işarettir. Başlığın kendisini User-Agent Nedir? yazımızda anlattık.
  • IP'den bağımsız hız sınırı. Yüzlerce IP'ye dağılmış bir trafik aynı parmak izini taşıyorsa, sayaç IP yerine parmak izine bağlanabilir. Dönen IP bu sayacı sıfırlamaz.
  • Bilinen araçları tanıma. Zararlı yazılım aileleri ve tarama araçları için derlenmiş parmak izi listeleri vardır; güvenlik ekipleri bunları günlüklerle eşleştirir.

Sınırları da vardır. Güncel bir tarayıcının değerini engelleyen site, o tarayıcıyı kullanan bütün ziyaretçilerini engeller. Tarayıcı güncellemeleri değeri değiştirir, kurumsal ağ geçitleri ve antivirüsler kendi parmak izlerini araya koyar. Bu yüzden TLS parmak izi tek başına karar verdirmez; IP itibarı ve davranışla birlikte puana giren sinyallerden biridir. Bu puanlamanın bütününü Bot Tespiti Nasıl Yapılır? yazımızda, Cloudflare'in katmanlarını ise Cloudflare Precursor yazımızda ele aldık.

Veri toplayan geliştirici için ne anlama geliyor?

Girişteki duruma dönelim. Python istemcisi User-Agent satırında Chrome olduğunu söylüyor, ClientHello ise OpenSSL olduğunu. Site bu çelişkiyi ilk pakette görüyor. Doğru tepki çelişkiyi gizlemeye çalışmak değil, ortadan kaldırmaktır:

  • Olmadığınız bir tarayıcı olduğunuzu iddia etmeyin. Betiğinizin User-Agent değeri betiği tanıtsın: bir ad, bir sürüm ve iletişim adresi. Dürüst kimlikle gelen, robots.txt dosyasına uyan ve yavaş çalışan bir istemciyi site sahibi tanıyıp izin listesine alabilir. Kuralları robots.txt Dosyası Nedir, Nasıl Okunur? yazımızda anlattık.
  • Resmi API varsa onu kullanın. API anahtarıyla gelen istekte kimliğiniz zaten bellidir; parmak izi tartışması ortadan kalkar.
  • Sayfa gerçekten tarayıcı istiyorsa gerçek tarayıcı kullanın. JavaScript ile çizilen bir sayfayı Playwright ile gerçek bir Chromium içinde açtığınızda istemciniz gerçekten o tarayıcıdır; başlıkları, TLS katmanı ve JavaScript ortamı birbiriyle tutarlıdır. Kurulumu Playwright Nedir ve Proxy ile Nasıl Kullanılır? yazımızda.
  • Hızı düşürün ve izin isteyin. Düzenli, büyük hacimli bir iş için site sahibine yazmak çoğu zaman en kalıcı çözümdür.

Engellenmenin diğer nedenlerini ve meşru çözümlerini Web Scraping'de Engellenmeden Veri Toplama Yöntemleri yazımızda topladık.

Kullanım alanları

  • Scraper'da 403 teşhisi: aynı IP ile tarayıcı geçiyor, betik geçemiyorsa fark büyük olasılıkla istemci kimliğindedir. Durum kodlarının ayrımı Scraping'de HTTP Hata Kodları yazımızda, genel kurgu veri kazıma çözümü sayfamızda.
  • Kendi sitenizde bot trafiğini ayırmak: günlüklerde parmak izine göre gruplama, IP'ye göre gruplamanın kaçırdığı dağıtık trafiği gösterir.
  • Kendi tarayıcınızı (crawler) tanıtmak: sabit bir kütüphane sürümü tutarlı bir parmak izi üretir, crawler'ınız günlüklerde böyle tanınır. Altyapı tarafı web crawler çözümü sayfamızda.
  • Proxy beklentisini doğru kurmak: Residential Proxy IP itibarını ve konumu değiştirir; istemcinizin kimliği sizin sorumluluğunuzda kalır.

Sık yapılan hatalar

  • User-Agent değiştirince istemcinin değiştiğini sanmak. Başlık bir metindir; ClientHello kütüphanenin davranışıdır ve başlıktan önce gider.
  • Proxy'den TLS parmak izini değiştirmesini beklemek. Tünel proxy'si IP'yi değiştirir, el sıkışmaya dokunmaz.
  • Chromium tabanlı tarayıcıda JA3 özetinin sabit kalmasını beklemek. Uzantı sırası her bağlantıda değişir; karşılaştırma için JA4 ya da sıralanmış uzantı listesi kullanın.
  • Yankı servisindeki sonucu sorgulamadan kabul etmek. HTTPS taraması yapan bir antivirüs ya da kurumsal ağ geçidi, servise kendi parmak izini gösterir.
  • Ölçümde 127.0.0.1 kullanmak. SNI gönderilmez, gerçek bağlantılardakinden farklı bir değer çıkar.

Karar rehberi

DurumÖneri
Proxy ile IP değişiyor, ilk istekte 403 alıyorsunuzİstemci kimliğine bakın: User-Agent ile kullandığınız kütüphane aynı şeyi söylüyor mu?
Kendi istemcinizin JA3 ya da JA4 değerini öğrenmek istiyorsunuzYankı servisi ya da yukarıdaki yerel dinleyici
Chrome trafiğini günlüklerde gruplamak istiyorsunuzJA3 değil JA4; uzantı karıştırmadan etkilenmez
Sayfa JavaScript ve gerçek tarayıcı istiyorGerçek bir tarayıcı otomasyonu (Playwright), makul hız, sitenin izin verdiği kapsam
Yankı servisi beklemediğiniz bir değer gösteriyorSertifikayı kimin imzaladığına bakın; arada TLS sonlandıran bir yazılım olabilir
Sitenizde dağıtık bot trafiği varHız sınırını IP yerine IP + parmak izi çiftine bağlayın, tek özete göre engellemeyin

Sıkça sorulan sorular

JA3 ile JA3S arasındaki fark nedir?

JA3 istemcinin ClientHello mesajından, JA3S sunucunun ServerHello mesajından üretilir. Sunucu farklı istemcilere farklı yanıt verdiği için JA3S tek başına sunucuyu tanımlamaz; aynı istemciye verilen yanıt ise hep aynıdır. Güvenlik ekipleri bu yüzden ikisini çift olarak kullanır.

VPN kullanmak TLS parmak izini değiştirir mi?

Hayır. VPN trafiği şifreli bir tünelden geçirir, ama site ile TLS el sıkışmasını yine sizin tarayıcınız ya da betiğiniz yapar. Site VPN sunucusunun IP adresini ve sizin istemcinizin parmak izini görür. İki aracın farkını Proxy ve VPN Farkı yazımızda anlattık.

TLS parmak izi beni kişisel olarak tanımlar mı?

Tek başına hayır. Aynı işletim sisteminde aynı tarayıcı sürümünü kullanan herkes aynı değeri üretir. Kişiye yaklaşan kimlik, bu değerin IP adresi, çerezler ve JavaScript katmanındaki sinyallerle birleştirilmesinden doğar.

Tarayıcıyı güncellemek parmak izini değiştirir mi?

Değiştirebilir. Yeni sürüm bir şifre takımını kaldırdığında ya da yeni bir uzantı eklediğinde liste ve özet değişir. Parmak izi listeleri bu yüzden sürüm sürüm tutulur.

Gizli pencere ya da çerezleri silmek TLS parmak izini etkiler mi?

Hayır. ClientHello mesajını tarayıcının TLS kütüphanesi üretir; geçmiş, çerezler ve pencere türü bu listelere girmez.

Parmak izim yüzünden engelleniyorsam ne yapmalıyım?

Önce ölçün: yankı servisinde hangi değerin göründüğüne ve arada TLS sonlandıran bir yazılım olup olmadığına bakın. Betik yazıyorsanız kimliğinizi dürüstçe belirtin, hızınızı düşürün ve sitenin resmi API'sini ya da izin kanalını kullanın. Sıradan bir tarayıcıyla engelleniyorsanız sorun büyük olasılıkla parmak izinde değil IP itibarındadır.

Özet

TLS parmak izi, şifreli bağlantının ilk ve şifresiz paketi olan ClientHello mesajından çıkar. JA3 bu mesajdaki beş listeyi sırasıyla birleştirip MD5 özetini alır; Chrome uzantı sırasını karıştırmaya başlayınca özet kararsızlaştı, JA4 listeleri sıralayarak bunu giderdi. Değer kişiyi değil yazılımı gösterir, başlıklardan önce gider ve tünel kuran hiçbir proxy ona dokunmaz: proxy IP'yi değiştirir, istemcinin kimliği sizde kalır. Tutarlı ve dürüst bir istemci kimliğinin yanına doğru IP türünü eklemek için proxy hizmetlerimize göz atabilirsiniz.

ChatGPT'ye sorClaude'a sor