Python Requests'te TLS Parmak İzi Engellemeleri Nasıl Düzeltilir

Başlıklarınız kusursuz olsa bile site sizi hâlâ engelliyorsa, engelleme ilk başlığınız ulaşmadan önce gerçekleşmiştir. TLS parmak izi, HTTP istemcinizi TLS el sıkışmasının şekline bakarak tanımlar ve Python requests kütüphanesi, yeryüzündeki hiçbir tarayıcının üretmediği bir el sıkışmaya sahiptir. Bunu bir user agent dizesini kopyalayarak düzeltemezsiniz. Bunu, el sıkışmanın kendisini bir tarayıcı gibi göstererek düzeltirsiniz. Bu yazı, kendi parmak izinizi nasıl okuyacağınızı, onu nasıl değiştireceğinizi ve bu sorunu karıştırıldığı iki sorundan nasıl ayırt edeceğinizi gösterir.
Neye ihtiyacınız var
- Python 3.10 veya daha yenisi. Örnekler önce sorunu göstermek için requests kütüphanesini, ardından onu düzeltmek için curl_cffi'yi kullanır.
- Bir terminal ve yaklaşık on dakika. Tanılama adımı iki istek alır ve herhangi bir kod değişikliği gerektirmez.
- CapSkip yalnızca son bölüm için gereklidir; ister loopback adresinde Local modda ister worker'larınızın erişebileceği bir makinede Server modda çalışsın. Her iki mod da bağlantı ayarları sayfasında açıklanır, bu yüzden başlamadan önce birini seçin.
TLS parmak izi aslında neyi okur
Her TLS bağlantısı bir ClientHello mesajıyla açılır. Bu mesaj; desteklediğiniz TLS sürümlerini, sunduğunuz şifreleme paketlerini (cipher suite), gönderdiğiniz uzantıları, kabul ettiğiniz eliptik eğrileri ve bunların tümünü koyduğunuz sırayı listeler. Bunların hiçbiri gizli değildir ve hiçbiri istek başına yapılandırılabilir değildir. Bu, HTTP istemcinizin hangi TLS kütüphanesine karşı derlendiğine göre belirlenir.
O listeyi hashleyin, istemci yazılımı için kararlı bir tanımlayıcı elde edersiniz. JA3, bu hash'in yaygın olarak kullanılan ilk sürümüydü. JA4 ise güncel olanıdır ve aynı tarayıcının her bağlantıda farklı bir parmak izi üretmemesi için hashlemeden önce ClientHello uzantılarını sıralar. Cloudflare ikisini de şöyle tarif eder: TLS istemcilerini bağlantıları nasıl başlattıklarına göre tanımlamanın bir yolu, ve bu değeri güvenlik duvarı kurallarına, analitiklere ve Workers'a sunar.
İşte bunun ayarladığınız herhangi bir başlıktan neden daha önemli olduğu. Python'ın gönderdiği haliyle OpenSSL, Chrome'un gönderdiği haliyle BoringSSL ve Firefox'un gönderdiği haliyle NSS, hepsi farklı ClientHello mesajları gönderir. Dolayısıyla Python gibi el sıkışırken Chrome olduğunu iddia eden bir istek, akıllıca bir tespit gerektiren ince bir işaret değildir. Bu, birbiriyle çelişen iki alandır ve bir bot kuralı bunları tek satırda karşılaştırabilir.
Adım 1: Herhangi bir şeyi değiştirmeden önce kendi parmak izinizi okuyun
TLS parmak izinin sorununuz olup olmadığını tahmin etmeyin, çünkü bunu ölçebilirsiniz. Bağlantınızda gördüğü JA3 hash'ini geri yansıtan herkese açık bir uç nokta vardır ve buna karşı iki isteği karşılaştırmak bir dakikadan az sürer.
# pip install requests
import requests
# The endpoint echoes back the handshake it received from you.
r = requests.get("https://tls.browserleaks.com/json", timeout=30)
seen = r.json()
print(seen["ja3_hash"]) # stable per TLS library, not per user agent
print(seen["ja3_text"]) # the raw cipher and extension listŞimdi aynı çağrıyı, başlıklarda bir tarayıcı user agent'ı ayarlanmış olarak tekrar çalıştırın ve hash'i ikinci kez okuyun. Değişmemiş olacaktır. Tek bir deneydeki tüm ders budur: user agent bir başlıkta yaşar, parmak izi ise el sıkışmada yaşar ve birincisini değiştirmek ikincisine hiçbir şey yapmaz. Bir engeli aşmak için user agent'ları döndürüyorsanız, işe yaramamasının nedeni budur.
Adım 2: curl_cffi ile gerçek bir tarayıcı gibi el sıkışın
Standart Python TLS yığınına bir Chrome ClientHello'su ürettiremezsiniz, çünkü uzantı kümesi ve sıralaması ayar olarak sunulmak yerine kütüphane derlemesine gömülüdür. Yapabileceğiniz şey farklı bir kütüphane kullanmaktır. curl_cffi paketi, belirli tarayıcı el sıkışmalarını yeniden üreten bir curl derlemesine bağlanır ve requests biçiminde bir API sunar; böylece kodunuzun geri kalanı neredeyse hiç değişmez.
# One dependency, no browser and no driver involved. pip install curl_cffi --upgrade
İşi yapan argüman impersonate'dir. El sıkışmanın hangi tarayıcı derlemesini kopyalayacağını seçer ve bir site güncel sürüm konusunda seçici hale geldiğinde belirli bir sürümü sabitleyebilirsiniz.
# pip install curl_cffi
import curl_cffi
# Same call as before, but the handshake now matches Chrome.
r = curl_cffi.get("https://tls.browserleaks.com/json", impersonate="chrome")
print(r.json()["ja3_hash"]) # a different hash from the requests run above
# Pin a version when a site rejects the current default.
r = curl_cffi.get("https://example.com/", impersonate="chrome124")
print(r.status_code)İki hash'i yan yana okuyun. Farklılarsa, taklit (impersonation) çalışıyor demektir ve değerli olan tek onay budur. Proje desteklenen hedefleri belgeliyor, bunlar arasında Chrome'un yanı sıra Safari ve iOS Safari derlemesi de var. Elle yazılmış JA3 dizeleri de kabul edilir, ancak neredeyse her durumda bakımı yapılan bir ön ayar (preset) bunu geride bırakır, çünkü tarayıcılar güncellendiğinde ön ayarlar da güncellenir.
Çalıştığında bir session'a geçin. Bağlantı yeniden kullanımını ve bir cookie jar'ı, requests ile istediğiniz aynı nedenlerle istersiniz.
# pip install curl_cffi
import curl_cffi
# One session, one handshake profile, cookies carried across calls.
s = curl_cffi.Session(impersonate="chrome")
s.get("https://example.com/login")
r = s.get("https://example.com/dashboard")
print(r.status_code, len(s.cookies))Adım 3: İstemcinin geri kalanını tutarlı tutun
Açıkça betikli (scripted) bir isteğe eklenmiş bir tarayıcı el sıkışması kendi başına bir tür uyumsuzluktur ve bir sinyali düzeltirken diğerlerini gürültülü bırakmak, bunun yanlış gitmesinin en yaygın yoludur. Üç şeyin seçtiğiniz profille uyuşması gerekir.
User agent'ınız, taklit hedefiyle aynı tarayıcı ailesini ve kabaca aynı sürümü belirtmelidir. Chrome gibi el sıkışırken Firefox olduğunuzu iddia etmek, hiçbir şey iddia etmemekten daha kötüdür, çünkü zayıf bir sinyali güvenilir bir sinyale dönüştürür.
Başlık sıranız önemlidir, HTTP/2 davranışınız da öyle. Tarayıcılar; kararlı bir başlık sırası, kararlı bir HTTP/2 ayar çerçeveleri (settings frames) kümesi ve kararlı bir sözde başlık (pseudo header) sıralaması gönderir; bunların hepsi TLS katmanı kadar temiz şekilde parmak izi verir. Başlıkları sözlüğünüzün üzerinde gezindiği rastgele sırayla göndermek, az önce yaptığınız işi boşa çıkarır. Bu, HTTP/2 katmanını istemci varsayılanlarınıza bırakmak yerine sizin için halleden, bakımı yapılan bir taklit ön ayarı için bir neden daha.
IP'niz, gönderdiğiniz trafik için mantıklı olmalıdır. Makine hızında tarayıcı biçimli istekler çalıştıran bir veri merkezi adresi, ayrı bir sinyaldir ve hiçbir el sıkışma bunu onarmaz. Seçeneklerin tam bir dökümü şu yazıda bulunur: CAPTCHA proxy rotasyonu, bu da rotasyonun bir worker havuzunun neresine ait olduğunu ele alır.
Sinyalleri sıralamak, böylece onları doğru sırada düzeltirsiniz
| Sinyal | Nerede okunur | Değiştirebilir misiniz |
|---|---|---|
| JA3 veya JA4 olarak hashlenen TLS ClientHello | Herhangi bir HTTP verisi gönderilmeden önce | Evet, TLS kütüphanesini değiştirerek |
| HTTP/2 ayarları ve sözde başlık sırası | Bağlantının ilk çerçeveleri | Evet, ve iyi bir ön ayar bunu sizin için yapar |
| Başlık adları, değerleri ve sırası | İsteğin kendisi | Evet, ve yanlış yapılması en kolay olanı budur |
| IP itibarı ve adres türü | Bağlantı kaynağı | Yalnızca bağlandığınız yeri değiştirerek |
| Canvas ve WebGL gibi tarayıcı çalışma zamanı sinyalleri | Gerçek bir sayfanın içinde, JavaScript çalıştıktan sonra | Bir tarayıcı çalıştırmıyorsanız geçerli değildir |
| İstek hızı ve gezinme deseni | Birçok istek boyunca | Evet, yavaşlayarak ve gittiğiniz yolları çeşitlendirerek |
Bu tabloyu enine değil, yukarıdan aşağıya işleyin. TLS parmak izi bağlantıda en erken gerçekleşir, bu yüzden bir savunmacının karar vermesi için en ucuz yerdir ve düzeltmenizin varması gereken ilk yer de burasıdır.
Bunların hiçbirinin düzeltmediği şeyler
Doğru bir el sıkışma bir giriş bileti değildir. Size güvenmemek için bir nedeni ortadan kaldırır; bu da sitenin sizi kapıda reddetmek yerine artık trafiğinizin geri kalanını değerlendirdiği anlamına gelir. Parmak izini düzelttikten sonra birçok site size hâlâ bir challenge gösterecektir ve bu, işin başarısız olduğunun bir işareti değil, beklenen sonuçtur.
Bu, dürüst sınırdır ve açıkça belirtilmeye değer. CapSkip TLS parmak izinizi değiştirmez ve bir gizlilik (stealth) katmanı değildir. Size sunulan CAPTCHA'yı çözer; bu, sorunun sonunda bir token olan kısmıdır. Sizinle yanıt gövdesi arasında duran şey bir Turnstile widget'ı veya bir challenge sayfasıysa, çözücü token'ı üretir ve mevcut istemciniz onu gönderir.
# pip install capskip
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# The challenge you were served, solved on your own machine.
result = solver.turnstile(
sitekey="YOUR_SITEKEY",
url="https://example.com/protected",
)
token = result["code"]
agent = result["userAgent"] # send this exact user agent with the tokenGeri döndürülen o user agent süs değildir. Turnstile, token'ı onu üreten tarayıcı kimliğine bağlar; bu nedenle tamamen geçerli bir token'ı farklı bir user agent altında göndermek, onun reddedilmesinin güvenilir bir yoludur. İkiliyi birlikte gönderin ve gönderim geçerli olsun. Cloudflare Turnstile çözücü sayfası, widget durumunu ve challenge sayfası durumunu ayrı ayrı ele alır, çünkü bunlar farklı girdilere ihtiyaç duyar.
Çözücüyü onun yerine bir sunucuda çalıştırmak
Parmak izi çalışması genellikle tek bir betikte değil, bir worker havuzunda karşımıza çıkar ve bir havuz bir loopback adresini paylaşmaz. Bağlantı ayarları her iki şekli de kapsar:
| Mod | Dinlediği adres | Ne zaman kullanılır |
|---|---|---|
| Yerel | 127.0.0.1, yalnızca o cihaz | Scraper'ınız ve çözücü aynı makinede çalışıyor |
| Sunucu | Ağ adresiniz veya genel IP'niz | Worker'lar, bir VPS veya barındırılan bir platform API üzerinden çağrı yapar |
SDK host'unu çözücü makineye yönlendirin, kodunuzda başka hiçbir şey değişmez; böylece bütün bir filo tek bir örneği paylaşabilir. Çağrı yapanlar kendi ağınızın dışında bulunduğunda statik bir genel IP önerilir. Ayrıntılar bağlantı ayarları altında bulunur; Server mode yine sizin donanımınızdır ve yine ölçümsüzdür (unmetered): yalnızca çözücünün nerede çalıştığını değiştirir, kime ait olduğunu asla değiştirmez.
FAQ
requests kütüphanesinin TLS parmak izini değiştirebilir miyim?
Faydalı bir şekilde değil. Alttaki SSL bağlamı (context) üzerinden şifreleme paketlerini yeniden sıralayabilir ve hash'i değiştirebilirsiniz, ancak bu yolla bir tarayıcı ClientHello'sunu yeniden üretemezsiniz, çünkü uzantı kümesi ve sıralaması yapılandırmadan değil kütüphane derlemesinden gelir. Sonunda elinizde hiçbir şeyle eşleşmeyen bir parmak izi kalır ki bu, Python ile eşleşmekten daha kötü bir konumdur. Bunun yerine kütüphaneyi değiştirin.
Gerçek bir tarayıcıyı yönetmek bu sorunu ortadan kaldırır mı?
TLS katmanı için evet, çünkü gerçek bir Chrome gerçek bir Chrome el sıkışması gönderir. Ayrıca worker başına yüzlerce megabayt ve çok daha yavaş bir isteğe mal olur; bu yüzden dar bir soruna ağır bir yanıttır. Sayfanın gerçekten JavaScript çalıştırmasına ihtiyacınız olduğunda bir tarayıcıya başvurun, yalnızca yanıt gövdesine ihtiyacınız olduğunda taklit eden bir HTTP istemcisine başvurun.
Engellemenin hız sınırlaması değil parmak izi olduğunu nasıl anlarım?
İyice yavaşlayın ve aynı istemciden tekrar deneyin. Bir hız sınırlaması beklediğinizde gevşer, genellikle ne kadar beklemeniz gerektiğini söyler ve sonrasında temiz bir şekilde geri döner. Bir TLS parmak izi engeli zamanlamayı hiç umursamaz; bu yüzden günün ilk isteği, yüzüncü istek gibi tam olarak başarısız olur. Soğuk bir istemci daha ilk isteğinde başarısız oluyorsa, çözümünüz beklemek değildir.
CapSkip, JA3 veya JA4 parmak izimi değiştirir mi?
Hayır, ve bununla ilgili hiçbir şeyin değişmemesi gerekir. CapSkip bir çözücüdür: ona bir challenge verirsiniz, o da size sahip olduğunuz donanımda ve çözüm başına ücret olmadan bir token verir. Parmak iziniz isteği yapan istemciye aittir, bu yüzden o kısım sizin işiniz olarak kalır. İki parça pratikte iyi uyum sağlar, çünkü tarayıcı biçimli bir istemciye düz bir ret yerine çözülebilir bir challenge sunulur.
En kısa hâli
Önce parmak izinizi okuyun, çünkü bu tek bir istek alır ve tartışmayı sonlandırır. User agent'ları döndürürken hash hiç değişmiyorsa, sorun el sıkışmadır ve hiçbir başlık bunu düzeltmez. Taklit eden bir istemciye geçin, user agent'ı, başlık sırasını ve HTTP/2 profilini taklit ettiğiniz şeyle tutarlı tutun, ardından IP'nize bakın. Sonrasında bir challenge görünmesi başarısızlık değil ilerlemedir ve bir yerel captcha çözücü onu bir token'a dönüştüren parçadır. Bir tanesini bir worker havuzuna bağlamayla ilgili notlar web scraping için CAPTCHA çözücü altında bulunur. Kodun kendisine geldiğinizde, Python captcha çözücü sayfası sonraki ihtiyacınız olan SDK ayrıntılarını taşır.
