curl-impersonate: บิลด์พิเศษของ curl ที่เลียนแบบเบราว์เซอร์หลักได้
(github.com/lwthiker)- curl-impersonate คือบิลด์ของ curl ที่ถูกปรับแต่งให้ทำ TLS และ HTTP handshake ให้ดูเหมือน Chrome, Edge, Safari, Firefox และสามารถใช้ได้ทั้งเป็นเครื่องมือ CLI และไลบรารีทดแทน
libcurl - ค่า Client Hello และการตั้งค่า HTTP/2 ของไคลเอนต์และไลบรารี HTTP ทั่วไปแตกต่างจากเบราว์เซอร์จริงอย่างมาก ทำให้บางเว็บเซอร์วิสระบุไคลเอนต์จาก TLS/HTTP handshake แล้วส่งเนื้อหาที่ต่างกันให้
- การใช้งานประกอบด้วยการใช้ NSS สำหรับ Firefox, BoringSSL สำหรับตระกูล Chrome, การเปลี่ยน TLS extensions, ตัวเลือก SSL, การตั้งค่า HTTP/2 และการใช้แฟล็กที่ไม่ใช่ค่าเริ่มต้น เช่น
--ciphers,--curvesรวมถึง header - เป้าหมายที่รองรับคือ Chrome 99~116, Android Chrome 99, Edge 99·101, Firefox 91 ESR~117, Safari 15.3·15.5 และแต่ละเป้าหมายมี wrapper script กับ target name ให้ใช้
- บน command line ให้รัน
curl_chrome116และในการผสานรวมแบบไลบรารีสามารถใช้curl_easy_impersonate()หรือใช้LD_PRELOADกับCURL_IMPERSONATEบน Linux เพื่อใช้กับแอปที่ใช้libcurlเดิมได้
ปัญหาที่ curl-impersonate แก้ไข
- curl-impersonate คือโปรเจกต์ที่สร้างบิลด์พิเศษของ curl เพื่อให้เลียนแบบเบราว์เซอร์หลัก 4 ตัว ได้แก่ Chrome, Edge, Safari, Firefox ได้
- สามารถทำ TLS และ HTTP handshake ได้ เหมือนกัน กับเบราว์เซอร์จริง
- วิธีใช้งานมีสองแบบ
- เครื่องมือ command line ที่คล้ายกับ curl ทั่วไป
- ไลบรารี ที่สามารถผสานรวมแทน
libcurlเดิมได้
ทำไมจึงจำเป็น
- เมื่อ HTTP client เชื่อมต่อกับเว็บไซต์ TLS จะทำ TLS handshake ก่อน
- ข้อความแรกของ TLS handshake คือ Client Hello และ Client Hello ที่ HTTP client กับไลบรารีส่วนใหญ่สร้างขึ้นแตกต่างจากเบราว์เซอร์จริงอย่างมาก
- หากเซิร์ฟเวอร์ใช้ HTTP/2 จะมีการทำ HTTP/2 handshake เพิ่มเติมนอกเหนือจาก TLS handshake และมีการแลกเปลี่ยนการตั้งค่าหลายอย่างในขั้นตอนนี้
- การตั้งค่า HTTP/2 ของ HTTP client และไลบรารีส่วนใหญ่ก็แตกต่างจากเบราว์เซอร์จริงเช่นกัน
- เว็บเซอร์วิสบางแห่งใช้ความแตกต่างเหล่านี้เพื่อระบุไคลเอนต์ที่เชื่อมต่อ และส่งเนื้อหาที่แตกต่างกันตามไคลเอนต์
- วิธีนี้เรียกว่า TLS fingerprinting และ HTTP/2 fingerprinting
- README ระบุว่าการใช้วิธีเหล่านี้อย่างแพร่หลายทำให้เว็บเปิดกว้างน้อยลง เป็นส่วนตัวน้อยลง และจำกัดกับเว็บไคลเอนต์บางตัวมากขึ้น
วิธีทำงาน
curlถูกแพตช์อย่างมากเพื่อให้ดูเหมือนเบราว์เซอร์- การเปลี่ยนแปลงหลักมีดังนี้
- เวอร์ชัน Firefox คอมไพล์ curl ด้วย NSS ซึ่งเป็นไลบรารี TLS ที่ Firefox ใช้ แทน OpenSSL
- เวอร์ชัน Chrome คอมไพล์ด้วย BoringSSL ซึ่งเป็นไลบรารี TLS ของ Google
- เปลี่ยนวิธีที่ curl ตั้งค่า TLS extensions และตัวเลือก SSL หลายรายการ
- เพิ่มการรองรับ TLS extensions ใหม่
- เปลี่ยนการตั้งค่าที่ใช้กับการเชื่อมต่อ HTTP/2
- รัน curl ด้วยแฟล็กที่ไม่ใช่ค่าเริ่มต้น เช่น
--ciphers,--curvesและ header-Hบางรายการ
- ผลลัพธ์คือจากมุมมองของเครือข่าย curl ดูเหมือนเบราว์เซอร์จริง
- คำอธิบายทางเทคนิคทั้งหมดอยู่ใน part a, part b
เบราว์เซอร์ที่รองรับและชื่อเป้าหมาย
- รายชื่อเบราว์เซอร์ที่รองรับมีให้ใน
browsers.jsonด้วย - เป้าหมายที่รองรับในตระกูล Chrome
- Chrome 99, 100, 101, 104, 107, 110, 116 on Windows 10
- Chrome 99 on Android 12
- Edge 99, 101 on Windows 10
- Safari 15.3 on MacOS Big Sur
- Safari 15.5 on MacOS Monterey
- เป้าหมายที่รองรับใน Firefox
- Firefox 91 ESR, 95, 98, 100, 102, 109, 117 on Windows 10
- แต่ละเป้าหมายที่รองรับมี target name และ wrapper script
- ตัวอย่าง: target
chrome116รันด้วย wrapper scriptcurl_chrome116 - ตัวอย่าง: target
ff117รันด้วย wrapper scriptcurl_ff117
- ตัวอย่าง: target
วิธีใช้งานพื้นฐาน
- สำหรับแต่ละเบราว์เซอร์ที่รองรับ จะมี wrapper script สำหรับรัน
curl-impersonateพร้อม header และแฟล็กที่จำเป็น - ตัวอย่างการรัน
curl_chrome116 https://www.wikipedia.org - หากเพิ่มแฟล็ก command line จะถูกส่งต่อไปยัง curl
- แฟล็กบางตัวอาจเปลี่ยน TLS signature ของ curl จนถูกตรวจจับได้
- wrapper script ใช้ชุด HTTP header เริ่มต้น
- หากต้องการเปลี่ยน header สามารถแก้ไข wrapper script ให้เหมาะกับวัตถุประสงค์ได้
- ตัวเลือกเพิ่มเติมอยู่ในวิธีใช้งานขั้นสูงสำหรับใช้
libcurl-impersonateเป็นไลบรารี
วิธีติดตั้งและการแจกจ่าย
- ด้วยเหตุผลทางเทคนิค
curl-impersonateมีสองเวอร์ชัน- เวอร์ชัน chrome: ใช้เลียนแบบ Chrome, Edge, Safari
- เวอร์ชัน firefox: ใช้เลียนแบบ Firefox
- ไบนารีที่คอมไพล์ไว้ล่วงหน้าสำหรับ Linux และ macOS Intel มีให้ที่ GitHub releases
- ก่อนใช้ไบนารีที่คอมไพล์ไว้ล่วงหน้า จำเป็นต้องติดตั้ง NSS และ CA certificates
- Ubuntu:
sudo apt install libnss3 nss-plugin-pem ca-certificates - Red Hat/Fedora/CentOS:
yum install nss nss-pem ca-certificates - Archlinux:
pacman -S nss ca-certificates - macOS:
brew install nss ca-certificates
- Ubuntu:
- ระบบต้องมี zlib ด้วย
- zlib แทบจะมีอยู่เสมอ แต่ในระบบขั้นต่ำบางระบบอาจไม่มี
- ไบนารี Linux ที่คอมไพล์ไว้ล่วงหน้าถูกบิลด์สำหรับระบบ Ubuntu
- หากเกิดข้อผิดพลาดในการตรวจสอบ certificate บนดิสทริบิวชันอื่น อาจต้องบอกตำแหน่ง CA certificates ให้ curl ทราบ
curl_chrome116 https://www.wikipedia.org --cacert /etc/ssl/certs/ca-bundle.crt - สำหรับการบิลด์จากซอร์ส ดู INSTALL.md
Docker และแพ็กเกจ
- มี Docker image สำหรับ Alpine Linux และฐาน Debian บน Docker Hub
- Docker image มีไบนารีและ wrapper script ทั้งหมดรวมอยู่ด้วย
- ตัวอย่าง
# Firefox version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-ff docker run --rm lwthiker/curl-impersonate:0.6-ff curl_ff109 https://www.wikipedia.org # Chrome version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-chrome docker run --rm lwthiker/curl-impersonate:0.6-chrome curl_chrome110 https://www.wikipedia.org - ผู้ใช้ Archlinux สามารถใช้แพ็กเกจ AUR ได้
- แพ็กเกจที่คอมไพล์ไว้ล่วงหน้า: curl-impersonate-bin, libcurl-impersonate-bin
- แพ็กเกจบิลด์จากซอร์ส: curl-impersonate-chrome, curl-impersonate-firefox
- Homebrew formula อย่างไม่เป็นทางการสำหรับ Mac มีให้เฉพาะ Chrome
brew tap shakacode/brew brew install curl-impersonate
การใช้งานขั้นสูงของ libcurl-impersonate
libcurl-impersonate.soคือ libcurl ที่คอมไพล์ด้วยการเปลี่ยนแปลงเดียวกับcurl-impersonateบน command line- มีฟังก์ชัน API เพิ่มเติม
CURLcode curl_easy_impersonate(struct Curl_easy *data, const char * target, int default_headers); - เมื่อเรียกด้วย target name เช่น
chrome116จะตั้งค่าตัวเลือกและ header ที่ wrapper script เคยตั้งให้ภายใน - หาก
default_headersเป็น 0 จะไม่ตั้งค่ารายการ HTTP header ในตัว- ผู้ใช้ต้องส่ง header เองด้วยตัวเลือก libcurl ปกติ
CURLOPT_HTTPHEADER
- ผู้ใช้ต้องส่ง header เองด้วยตัวเลือก libcurl ปกติ
curl_easy_impersonate()ตั้งค่าตัวเลือก libcurl หลายรายการCURLOPT_HTTP_VERSIONCURLOPT_SSLVERSION,CURLOPT_SSL_CIPHER_LIST,CURLOPT_SSL_EC_CURVES,CURLOPT_SSL_ENABLE_NPN,CURLOPT_SSL_ENABLE_ALPN- ตัวเลือกไม่มาตรฐานสำหรับโปรเจกต์
CURLOPT_HTTPBASEHEADER - ตัวเลือก HTTP/2 ไม่มาตรฐานสำหรับโปรเจกต์
CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER,CURLOPT_HTTP2_NO_SERVER_PUSH - ตัวเลือก TLS ไม่มาตรฐานสำหรับโปรเจกต์
CURLOPT_SSL_ENABLE_ALPS,CURLOPT_SSL_SIG_HASH_ALGS,CURLOPT_SSL_CERT_COMPRESSION,CURLOPT_SSL_ENABLE_TICKET - ตัวเลือก TLS ไม่มาตรฐานสำหรับโปรเจกต์
CURLOPT_SSL_PERMUTE_EXTENSIONS
- หลังจากนั้น หากเรียก
curl_easy_setopt()ด้วยตัวเลือกใดตัวเลือกหนึ่งข้างต้น จะเขียนทับค่าที่curl_easy_impersonate()ตั้งไว้
การนำไปใช้กับแอป libcurl เดิม
- หากแอปพลิเคชันใช้
libcurlอยู่แล้ว บน Linux สามารถใช้LD_PRELOADเพื่อแทนที่ไลบรารีเดิมขณะรันไทม์ได้ - ตั้งค่าตัวแปรสภาพแวดล้อม
CURL_IMPERSONATEเพื่อใช้การเลียนแบบอัตโนมัติLD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 my_app CURL_IMPERSONATEมีผลสองอย่าง- เมื่อสร้าง curl handle ใหม่ด้วย
curl_easy_init()จะเรียกcurl_easy_impersonate()โดยอัตโนมัติ - หลังจาก
curl_easy_reset()ก็จะเรียกcurl_easy_impersonate()โดยอัตโนมัติด้วย
- เมื่อสร้าง curl handle ใหม่ด้วย
- หากต้องควบคุม HTTP header อย่างละเอียด ให้ปิดรายการ header ในตัวด้วย
CURL_IMPERSONATE_HEADERS=noแล้วตั้งค่าเองLD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 CURL_IMPERSONATE_HEADERS=no my_app - วิธี
LD_PRELOADใช้กับ curl เองไม่ได้- เพราะเครื่องมือ curl เขียนทับการตั้งค่า TLS
- สำหรับ curl ต้องใช้ wrapper script
โครงสร้าง repository และการมีส่วนร่วม
- repository มีโฟลเดอร์หลักสองโฟลเดอร์
- ตัวอย่างในไดเรกทอรี Firefox มีไฟล์ต่อไปนี้
- Dockerfile: ใช้บิลด์
curl-impersonateพร้อม dependency ทั้งหมด - curl_ff91esr, curl_ff95, curl_ff98: wrapper script ที่รันด้วยแฟล็กที่ถูกต้อง
- curl-impersonate.patch: แพตช์หลักที่ทำให้ curl ใช้ TLS extensions แบบเดียวกับ Firefox และทำให้คอมไพล์แบบ static กับ libnghttp2 และ libnss
- Dockerfile: ใช้บิลด์
- tests/signatures คือฐานข้อมูล YAML ของ signature เบราว์เซอร์ที่รู้จักและเลียนแบบได้
- แพตช์ curl จริงถูกดูแลใน repository แยกที่ fork จาก upstream curl
- การเปลี่ยนแปลงสำหรับ Firefox อยู่ใน branch impersonate-firefox
- การเปลี่ยนแปลงสำหรับ Chrome อยู่ใน branch impersonate-chrome
1 ความคิดเห็น
ความเห็นใน Hacker News
มี fork ที่ได้รับการดูแลอย่างต่อเนื่อง และปรับปรุงจากต้นฉบับอยู่พอสมควร: https://github.com/lexiforest/curl-impersonate
มี binding สำหรับผู้ใช้ Python ด้วย: https://github.com/lexiforest/curl_cffi
ทั้งที่ในเชิงย้อนแย้ง คำขอนั้นน่าจะกินทรัพยากรเซิร์ฟเวอร์น้อยกว่าเบราว์เซอร์ที่ผ่านการรับรองเสียอีก เลยอดคิดไม่ได้ว่านี่คือ ดิสโทเปีย ที่เคยถูกเตือนกันไว้ตั้งแต่ยุค 90 หรือเปล่า และก็สงสัยว่าจะมีใครที่นี่กล้าเอ่ยชื่อบริษัทเดียวที่สร้างสถานการณ์นี้ขึ้นมา ทั้งที่ยังวางตัวเป็นผู้มีคุณูปการต่อชุมชนโอเพนซอร์ส/แฮ็กเกอร์ผู้แสนดี
หวังว่า Ladybird จะมีแรงส่งมากขึ้นในอนาคต ตอนนี้มันใช้ cURL ทางการสำหรับระบบเครือข่ายอยู่ ซึ่งแนวทางนี้อาจมีอุปสรรคได้
ตัวอย่างเช่น เท่าที่รู้ cURL ยังมีข้อจำกัดอยู่บางอย่าง และยังจัดการ WebSocket บน h2 ไม่ได้ ในทางกลับกัน หากมีเอนจินเบราว์เซอร์ที่กำลังเติบโตขึ้น ทราฟฟิกปกติก็อาจมีลายนิ้วมือแบบเดียวกับ cURL ปกติ ทำให้ช่องทางการเก็บลายนิ้วมือนี้หายไปได้
มันยังเป็นสนามทดสอบที่ดีด้วยว่า cURL ขาดความสามารถอะไรไปบ้างเมื่อวัดกับเวิร์กโฟลว์ของเบราว์เซอร์จริง
ในช่วงไม่กี่เดือนที่ผ่านมา ระดับของ การเก็บลายนิ้วมือ และการ “ฉวยใช้” พฤติกรรมที่นิยามโดยการติดตั้งใช้งานเพิ่มขึ้นมาก และดูเหมือนจะเป็นความพยายามฆ่าเอนจินเบราว์เซอร์ตัวอื่น ผู้เล่นรายใหญ่เดิมไม่ชอบการแข่งขันเลยแม้แต่น้อย
ฝั่งนั้นพยายามห่อเรื่องนี้ว่าเป็น “DDoS จาก AI บอต” แต่ก็น่าสงสัยว่าในนั้นมีสัดส่วนเท่าไรที่เป็นเรื่องที่พวกเขาก่อขึ้นเอง
ฉันชอบ curl ตัวนี้ แต่ก็กังวลว่าถ้าส่วนประกอบบางตัวต้องทำหน้าที่เป็นตัว “หลอก” เพื่อ “ไล่ตามให้ทัน” มันจะสะสมภาระด้าน “ความเข้ากันได้” ที่ดูแลรักษายาก
ในอุดมคติ เราควรจะแค่พูดว่า “สวัสดี ฉันคือ curl ขอเข้าได้ไหม”
แน่นอนว่าปัญหาอยู่ที่เซิร์ฟเวอร์ที่เคร่งเรื่องการแต่งกาย และปัญหานั้นก็เกิดขึ้นเพราะมีพวกนักต้มตุ๋นปลอมตัวเข้ามาอีกที เลยวนเป็นปัญหาแบบไก่กับไข่
[0] https://cybershow.uk/episodes.php?id=39
อีกทั้งการออกแบบใหม่ด้วย C++ ซึ่งพิสูจน์ได้ว่าไม่ปลอดภัย ก็เป็นความเสี่ยงด้านความปลอดภัยครั้งใหญ่
เขาตั้งค่า
IP_TTLให้ ค่า TTL ตรงกับแพลตฟอร์มที่ต้องการปลอมเป็นด้วยหรือเปล่า?ถ้าไม่ ก็ยังพอเก็บลายนิ้วมือได้ในชั้น IP ด้วยเช่นกัน ถ้าค่า TTL ในชั้น IP ต่ำกว่า 64 ก็ชัดเจนว่าไม่ได้รันอยู่บน Windows สมัยใหม่ หรือไม่ก็เป็น Windows สมัยใหม่ที่เปลี่ยนค่า TTL เริ่มต้นไปแล้ว เพราะแพ็กเก็ตจาก Windows สมัยใหม่เริ่มต้นด้วย TTL 128 ขณะที่แพลตฟอร์มส่วนใหญ่อื่น ๆ เริ่มที่ 64
และเพราะแพลตฟอร์มอื่นก็สื่อสารผ่านอินเทอร์เน็ตได้ปกติ แพ็กเก็ต IP ที่มาจาก Windows สมัยใหม่จึงควรถูกมองเห็นจากปลายทางด้วย TTL อย่างน้อย 64 หรืออาจมากกว่า 64 เล็กน้อย อย่างไรก็ตาม การเก็บลายนิ้วมือในชั้น IP นั้นทำได้ยาก แต่ไม่ถึงกับเป็นไปไม่ได้
https://en.wikipedia.org/wiki/TCP/IP_stack_fingerprinting
เดี๋ยวก่อน ถ้า TLS handshake ดูต่างออกไป อย่างนั้นก็น่าจะกรองทราฟฟิกที่อ้างว่าเป็นเว็บเบราว์เซอร์ แต่จริง ๆ เป็นสคริปต์ Python/PHP ได้ตั้งแต่ระดับ nginx ไม่ใช่หรือ?
เช่น ใช้ user agent ของ Chrome แต่จริง ๆ ไม่ใช่เบราว์เซอร์ ถ้าบอตทราฟฟิกอันตรายส่วนใหญ่เข้าข่ายนี้ ก็น่าจะดีถ้าบล็อกมันไปได้เลย
รายละเอียดการทำงานมีอธิบายไว้ที่ https://github.com/FoxIO-LLC/ja4/blob/main/technical_details... และยังมีโมดูลสำหรับ Nginx ด้วย: https://github.com/FoxIO-LLC/ja4-nginx-module
ถ้าตอนนี้คุณไม่ได้กำลังถูกโจมตีอยู่จริง ก็ไม่ควรใช้ allowlist ของ TLS fingerprint
เป็นเครื่องมือที่ยอดเยี่ยม แต่ไม่ควรสำคัญเลยว่าฝั่งไคลเอนต์จะเป็นเบราว์เซอร์หรือไม่ ความจริงที่ว่ามีความจำเป็นต้องใช้เครื่องมือแบบนี้ในโลกความเป็นจริงทำให้รู้สึกขมขื่น
พอลองเพิ่มส่วนขยายเปลี่ยน user agent ใน Chrome แล้วสลับเป็น IE ก่อนลองใหม่ ก็ใช้งานได้ และฟังก์ชันทั้งหมดของเว็บก็ทำงานปกติดี เลยทั้งขมขื่นทั้งหงุดหงิด คงเป็นไปได้ว่าหน่วยงานรัฐนั้นสร้างเว็บไซต์นี้ไว้เมื่อ 25 ปีก่อนแล้วไม่เคยอัปเดตอีกเลย
การทำตัวเป็นผู้เฝ้าประตู แบบนี้ก็ทำให้ฉันรู้สึกขมขื่นเหมือนกัน เท่าที่ฉันเข้าใจ เบราว์เซอร์หรือ user agent แบบทำเองจะไม่มีวันใช้งานกับเว็บตระกูล Cloudflare ได้เลย จนกว่าจะมีอิทธิพล เงิน หรือจำนวนผู้ใช้มากพอที่จะไปโน้มน้าวพวกเขาได้
เครื่องมือนี้ค่อนข้างดีสำหรับ งานเรดทีม ถ้าเอาไปใช้ร่วมกับ bash script เล็ก ๆ และ GNU parallel
มันมีประโยชน์ตอนแมป HTTPS endpoint ภายในช่วงที่อยู่ตามขอบเขตที่กำหนดไว้ ซึ่งด้วยเหตุผลหลายอย่างจะตอบสนองเฉพาะกับเบราว์เซอร์จริง ๆ หรือไม่ก็ต้องมีการตั้งค่า SNI ที่ถูกต้องถึงจะตอบกลับได้ และยังใช้ตัวเลือก curl ปกติอย่าง
-Hสำหรับปลอม header ได้เหมือนเดิมโพสต์ Show HN ตอนนั้น: https://news.ycombinator.com/item?id=30378562
ทุกครั้งที่ของแบบนี้ถูกโพสต์ที่นี่ ฉันจะรู้สึกก้ำกึ่งอยู่เสมอ ด้านหนึ่งก็ดีที่มันบอกว่าผู้คนยังมีความขบถและความเป็นอิสระหลงเหลืออยู่นิดหน่อย
แต่อีกด้านหนึ่งก็เหมือนกับโปรเจกต์อื่น ๆ ที่ถูกมองว่า “อิสรภาพคือความไม่เสถียร” ถ้ามันดึงดูดความสนใจที่ไม่พึงประสงค์ ก็อาจทำให้คนที่พึ่งพามันอยู่เจอสถานการณ์ที่แย่ลงได้ การพัฒนาเบราว์เซอร์นั้นยาก และเจ้าตลาดเดิม ๆ ก็กำลังทำให้มันยากขึ้นเรื่อย ๆ
ฉันไม่มองว่านี่เป็นเรื่องของความขบถ การที่ซอฟต์แวร์ถูกระบุลายนิ้วมือได้กำลังกัดกร่อนทั้ง ความเป็นส่วนตัว และความหลากหลายของซอฟต์แวร์
คิดถึงอยู่บ้างกับ ยุคที่เรียบง่ายกว่า ซึ่งเว็บไซต์ถ้ามองว่าบอทโอเคก็ปล่อยผ่าน ถ้าไม่ชอบก็แค่บล็อก user agent
ถ้ามันมีแค่แพตช์ 3 ตัวกับ shell wrapper Daniel ก็น่าจะยอมลงมือเขียนได้ สำหรับฉัน นี่ควรเข้าไปอยู่ใน curl เมนไลน์ อย่างแน่นอน