- เมื่อส่งข้อความใน ChatGPT จะมีการรัน โปรแกรม Cloudflare Turnstile ซึ่งตรวจสอบไม่เพียงลายนิ้วมือเบราว์เซอร์ แต่รวมถึง สถานะของแอปพลิเคชัน React ด้วย
- โปรแกรมที่ถูกถอดรหัสแล้วเก็บข้อมูล 55 คุณสมบัติ และทำกระบวนการตรวจสอบที่แบ่งเป็น 3 ชั้น ได้แก่ เบราว์เซอร์ เครือข่าย และแอปพลิเคชัน
- จะผ่านได้เฉพาะในสภาพแวดล้อม SPA จริงที่ React เรนเดอร์เสร็จสมบูรณ์แล้วเท่านั้น ทำให้ เบราว์เซอร์แบบ headless หรือคำขอจากบอตแบบง่ายล้มเหลว
- ลายนิ้วมือที่เก็บรวบรวมจะถูกเข้ารหัสและแปลงเป็น
OpenAI-Sentinel-Turnstile-Token โดยยังมีโมดูล Signal Orchestrator และ Proof of Work ทำงานเพิ่มเติม
- มีเพียงเซิร์ฟเวอร์ Cloudflare เท่านั้นที่รู้คีย์ถอดรหัส ทำให้ เส้นแบ่งด้านความเป็นส่วนตัวถูกกำหนดด้วยนโยบาย ไม่ใช่ด้วยเทคโนโลยี
วิเคราะห์โครงสร้างการทำงานของ Cloudflare Turnstile ระหว่างการส่งข้อความใน ChatGPT
- ทุกครั้งที่ส่งข้อความใน ChatGPT จะมีการรัน โปรแกรม Cloudflare Turnstile ภายในเบราว์เซอร์โดยอัตโนมัติ
- จากผลการถอดรหัสโปรแกรม Turnstile 377 ชุดจากทราฟฟิกเครือข่าย พบว่าไม่ได้หยุดแค่การเก็บลายนิ้วมือเบราว์เซอร์ทั่วไป แต่ยัง ตรวจสอบสถานะของแอปพลิเคชัน React ด้วย
- บอตที่ปลอมแปลงเพียงลายนิ้วมือเบราว์เซอร์ไม่สามารถผ่านได้ และต้องเรนเดอร์ SPA (single-page application) ของ ChatGPT ให้สมบูรณ์จึงจะผ่านการตรวจสอบ
โครงสร้างการเข้ารหัสและกระบวนการถอดรหัส
- ไบต์โค้ดของ Turnstile ถูกส่งมาในฟิลด์
turnstile.dx ของการตอบกลับจากเซิร์ฟเวอร์ และถูกเข้ารหัสเป็น สตริง base64 ยาว 28,000 อักขระในทุกคำขอ
- ชั้นเข้ารหัสด้านนอกสามารถถอดรหัสได้ด้วยโทเค็น
p และการดำเนินการ XOR โดยทั้งสองค่านี้ถูกแลกเปลี่ยนกันภายในการเรียก HTTP เดียวกัน
- ผลลัพธ์ที่ถอดรหัสแล้วเป็นไบต์โค้ดในรูป JSON ซึ่งประกอบด้วย คำสั่ง VM 89 รายการ
- ภายในยังมี blob ที่เข้ารหัสเพิ่มเติมขนาด 19KB ซึ่ง blob นี้ถูกเข้ารหัสด้วยคีย์ XOR คนละชุด
- คีย์ถูกฝังเป็น ค่า float literal (เช่น 97.35) ภายในไบต์โค้ด และเซิร์ฟเวอร์เป็นผู้สร้างแล้วส่งมายังเบราว์เซอร์
- ยืนยันการถอดรหัสเป็น JSON ที่ใช้ได้จริงด้วยวิธีเดียวกันในคำขอทั้ง 50 ครั้ง
- กระบวนการถอดรหัสทั้งหมดประกอบด้วย 5 ขั้นตอนดังนี้
- อ่านโทเค็น
p จากคำขอ
- อ่าน
turnstile.dx จากการตอบกลับ
XOR(base64decode(dx), p) → สร้างไบต์โค้ดชั้นนอก
- ดึงคีย์จากอาร์กิวเมนต์ตัวสุดท้ายของคำสั่งแบบ 5 อาร์กิวเมนต์ที่อยู่หลัง blob 19KB
XOR(base64decode(blob), str(key)) → ถอดรหัสโปรแกรมภายใน (417~580 คำสั่ง)
รายการตรวจสอบของโปรแกรมที่ถอดรหัสแล้ว
- โปรแกรมภายในทำงานบน custom VM ที่มี 28 คำสั่ง (opcode) และตำแหน่งแอดเดรสของรีจิสเตอร์แบบ floating-point จะถูกสุ่มใหม่ในทุกคำขอ
- เก็บข้อมูล 55 คุณสมบัติ (property) โดยตัวอย่างทั้ง 377 ชุดมีรายการเหมือนกันทั้งหมด
-
Layer 1: ลายนิ้วมือเบราว์เซอร์
- คุณสมบัติที่เกี่ยวกับ WebGL 8 รายการ:
UNMASKED_VENDOR_WEBGL, UNMASKED_RENDERER_WEBGL, WEBGL_debug_renderer_info เป็นต้น
- ข้อมูลหน้าจอ 8 รายการ:
colorDepth, pixelDepth, width, height, availWidth, availHeight, availLeft, availTop
- ฮาร์ดแวร์ 5 รายการ:
hardwareConcurrency, deviceMemory, maxTouchPoints, platform, vendor
- การวัดฟอนต์ 4 รายการ: สร้าง div ที่ซ่อนไว้แล้ววัดขนาดการเรนเดอร์ด้วย
fontFamily, fontSize, getBoundingClientRect, innerText
- การสำรวจ DOM 8 รายการ:
createElement, appendChild, removeChild, style, position, visibility, ariaHidden เป็นต้น
- ที่เก็บข้อมูล 5 รายการ:
storage, quota, estimate, setItem, usage
- ผลลัพธ์จะถูกเก็บไว้ใน
localStorage ที่คีย์ 6f376b6560133c2c เพื่อคงอยู่ข้ามการรีโหลดหน้า
-
Layer 2: เครือข่าย Cloudflare
- เฮดเดอร์จาก edge 5 รายการ:
cfIpCity, cfIpLatitude, cfIpLongitude, cfConnectingIp, userRegion
- ค่าเหล่านี้มีอยู่ได้ผ่านเครือข่าย Cloudflare เท่านั้น ดังนั้น บอตที่เข้าถึง origin server โดยตรง จะมีค่าขาดหายหรือไม่ตรงกัน
-
Layer 3: สถานะแอปพลิเคชัน
- โครงสร้างภายในของ React 3 รายการ:
__reactRouterContext, loaderData, clientBootstrap
- รายการเหล่านี้จะมีอยู่ก็ต่อเมื่อแอปพลิเคชัน React ของ ChatGPT ถูกเรนเดอร์ครบถ้วนและ SSR hydration เสร็จสมบูรณ์ เท่านั้น
- เบราว์เซอร์แบบ headless ที่โหลดแค่ HTML หรือไม่รัน JS bundle รวมถึงเฟรมเวิร์กบอตที่ไม่ได้รัน React จริง จะล้มเหลว
กระบวนการสร้างโทเค็น
- หลังเก็บ 55 คุณสมบัติแล้ว โปรแกรมจะถอดรหัส blob เข้ารหัสขนาด 116 ไบต์ แล้วรันคำสั่งสุดท้าย 4 รายการ
JSON.stringify(fingerprint) → store → XOR(json, key) → RESOLVE
- ค่าผลลัพธ์จะถูกแปลงเป็นเฮดเดอร์
OpenAI-Sentinel-Turnstile-Token และแนบไปกับทุกคำขอสนทนา
องค์ประกอบเพิ่มเติมของ Sentinel
- นอกจาก Turnstile แล้ว ยังมีโมดูลตรวจสอบเพิ่มเติมอีก 2 ตัว
-
Signal Orchestrator
- ประกอบด้วยคำสั่ง 271 รายการ
- ติดตั้ง event listener สำหรับ
keydown, pointermove, click, scroll, paste, wheel
- ติดตามพร็อพเพอร์ตี
window.__oai_so_* จำนวน 36 รายการเพื่อมอนิเตอร์ จังหวะการพิมพ์ ความเร็วเมาส์ รูปแบบการเลื่อน ช่วงเวลาที่ไม่ได้ใช้งาน และเหตุการณ์วางข้อความ
- นอกเหนือจากการเก็บลายนิ้วมือแล้ว ยังทำหน้าที่เป็น ชั้นตรวจสอบชีวมิติจากพฤติกรรม
-
Proof of Work
- อิงตาม ลายนิ้วมือ 25 ฟิลด์ + SHA-256 hashcash
- ระดับความยากเป็นเลขสุ่มสม่ำเสมอในช่วง 400K~500K และ 72% ถูกแก้ได้ภายใน 5ms หรือน้อยกว่า
- มีแฟล็กตรวจจับแบบไบนารี 7 รายการ ได้แก่
ai, createPRNG, cache, solana, dump, InstallTrigger, data (ตัวอย่างทั้ง 100 ชุดเป็น 0 ทั้งหมด)
- เพิ่มต้นทุนด้านการคำนวณ แต่ ไม่ใช่มาตรการป้องกันหลัก
ผู้ที่สามารถถอดรหัสโทเค็นได้และความหมายด้านความปลอดภัย
- เนื่องจากคีย์ XOR ของโปรแกรมภายในถูกสร้างโดยเซิร์ฟเวอร์และฝังอยู่ในไบต์โค้ด จึงมีเพียง เซิร์ฟเวอร์ที่สร้าง
turnstile.dx เท่านั้นที่รู้คีย์
- เส้นแบ่งด้านความเป็นส่วนตัวระหว่างผู้ใช้กับผู้ดูแลระบบถูกกำหนดโดย การตัดสินใจเชิงนโยบาย ไม่ใช่ข้อจำกัดทางคริปโตกราฟี
- วัตถุประสงค์ของการทำให้อ่านยากมีดังนี้
- ซ่อนรายการข้อมูลลายนิ้วมือจากการวิเคราะห์แบบสถิต
- ป้องกันไม่ให้ผู้ดูแลเว็บไซต์ (OpenAI) อ่านค่าลายนิ้วมือดิบได้โดยตรง
- ทำให้แต่ละโทเค็นมีเอกลักษณ์เพื่อป้องกันการ reuse (replay)
- ทำให้ภายนอกสังเกตได้ยากเมื่อ Cloudflare เปลี่ยนรายการตรวจสอบ
- อย่างไรก็ตาม การเข้ารหัสนี้ใช้ คีย์และการดำเนินการ XOR ภายในสตรีมข้อมูลเดียวกัน จึงเป็นเพียง การทำให้อ่านยากเพื่อป้องกันการวิเคราะห์ เท่านั้น
สถิติการเก็บและการวิเคราะห์
| รายการ |
ค่า |
| โปรแกรมที่ถอดรหัสแล้ว |
377/377 (100%) |
| ผู้ใช้ที่ไม่ซ้ำกันที่สังเกตได้ |
32 |
| จำนวนคุณสมบัติต่อโปรแกรม |
55 (เหมือนกันทั้งหมด) |
| จำนวนคำสั่ง |
417–580 (เฉลี่ย 480) |
| คีย์ XOR (50 ตัวอย่าง) |
41 |
| คุณสมบัติของ Signal Orchestrator |
36 |
| ฟิลด์ของ Proof of Work |
25 |
| เวลาแก้ PoW |
72% ภายใน 5ms หรือน้อยกว่า |
ระเบียบวิธีการวิเคราะห์
- ใช้เฉพาะ ทราฟฟิกที่เก็บรวบรวมด้วยขั้นตอนที่ชอบด้วยกฎหมาย
- ไม่มีการเปิดเผยข้อมูลผู้ใช้ส่วนบุคคล
- ทราฟฟิกทั้งหมดถูกสังเกตภายใต้ ความยินยอมของผู้เข้าร่วม
- ทำ manual deobfuscation และถอดรหัสแบบออฟไลน์ กับ Sentinel SDK (
sdk.js, 1,411 บรรทัด)
- การถอดรหัสดำเนินการในสภาพแวดล้อมออฟไลน์โดยใช้ Python
1 ความคิดเห็น
ความคิดเห็นใน Hacker News
สวัสดีครับ ผม Nick ทำงานอยู่ใน ทีม Integrity ของ OpenAI
การตรวจสอบครั้งนี้เป็นส่วนหนึ่งของมาตรการป้องกัน การใช้งานในทางที่ผิด ของแพลตฟอร์ม เช่น บอต การสแครป และการฉ้อโกง
เป้าหมายคือจัดสรรทรัพยากร GPU ให้ผู้ใช้จริงก่อน เพื่อให้ยังคงเปิดให้ผู้ใช้ฟรีและผู้ที่ไม่ได้ล็อกอินเข้าถึงได้ต่อไป
เรากำลังติดตามเวลาโหลดหน้าเว็บ เวลาจนถึงโทเค็นแรก ขนาดเพย์โหลด และโฟกัสที่การ ลดโอเวอร์เฮด ของมาตรการป้องกันให้น้อยที่สุด
สำหรับผู้ใช้ส่วนใหญ่ผลกระทบมีน้อยมาก และมีเพียงส่วนน้อยมากที่อาจเจอความล่าช้าเล็กน้อย
นอกจากนี้ เรายังประเมินการปรับปรุงความแม่นยำอย่างต่อเนื่อง เพื่อให้การใช้งานในทางที่ผิดทำได้ยากขึ้น พร้อมลด อัตราการตรวจจับผิดพลาด ไปด้วย
ผมเข้าใจคำอธิบายของ Nick แต่ก็สงสัยว่าโลกที่ต้องเลือกระหว่าง ความเป็นส่วนตัวกับการใช้งานได้จริง จะเป็นแบบนี้ต่อไปหรือไม่
มีทั้งอาการพิมพ์หน่วง การเรนเดอร์สะดุด ไปจนถึงค้างสนิท
เจอเหมือนกันทั้งบน iPhone 16 Safari และ MacBook Pro M3 Chrome
มีการแซวให้ลองรันเครื่องมือที่ละเมิดความเป็นส่วนตัวของ Cloudflare แล้วเอาผลมาแชร์ดู
ผมเป็นสมาชิก Pro และทั้งทีมใช้จ่ายเกิน 2,000 ดอลลาร์ต่อเดือน
แต่เวลาใช้ VPN (Mullvad) อินเทอร์เฟซ Chat มักหลุดหรือตั้งเวลาเกินบ่อย แม้จะล็อกอินอยู่แล้วก็ตาม
ถ้าเป็นผู้ใช้แบบเสียเงิน ก็น่าจะใช้งานได้เสถียรโดยไม่ควรถูกกระทบจากการใช้ VPN
มีการบ่นว่า Cloudflare ทำให้ใช้งานเว็บแทบไม่ได้เลย เพียงเพราะมองว่าเบราว์เซอร์หรือ IP ‘น่าสงสัย’
ถึงขั้นบอกว่าถูกโยนเข้าไปอยู่ใน นรกแคปช่า แค่เพราะใช้ Firefox
คนที่ให้ความสำคัญกับความเป็นส่วนตัว เช่น ใช้ VPN เบราว์เซอร์สายความเป็นส่วนตัว หรือช่วง IP ที่พบไม่บ่อย กลับโดนมากที่สุด
แต่บอตกลับหลบตัวกรองพวกนี้ได้ง่าย
ตัวเองก็ใช้ Firefox แต่เจอแคปช่าในระดับปกติเท่านั้น
ปิด CGNAT อยู่ เลยสงสัยว่านั่นอาจเป็นตัวสร้างความแตกต่างหรือเปล่า
ทั้งที่ธนาคารยืนยันตัวตนไปแล้ว แต่เว็บยังแยกไม่ออกว่าเป็นคนหรือไม่ ก็เข้าใจได้ยาก
ดูเหมือนว่า OpenAI พยายามป้องกันไม่ให้คนเอา ChatGPT ฟรี สำหรับผู้ใช้ที่ไม่ได้ล็อกอินไป ใช้งานในทางที่ผิดเหมือนเป็น API ฟรี
บนแอป Android มีการตรวจ Play Integrity แต่แอป Claude แค่ให้ล็อกอินและไม่มีการตรวจแบบนี้
แค่พิมพ์คำถามโดยไม่มีคุกกี้หรือบัญชี ก็ได้คำตอบกลับมา เลยรู้สึกแปลกใจ
เพราะค่าบริการแบบสมาชิกถูกกว่า API มาก จึงจำเป็นต้องป้องกันการใช้งานในทางที่ผิด
การตรวจแค่ระดับโหลด SPA ไม่ใช่อุปสรรคใหญ่ และคนที่รู้เรื่องนี้ก็น่าจะมีเทคนิคหลบได้อยู่แล้ว
บริษัทอื่น ๆ ก็สร้าง ระบบแอนติบอต ของตัวเองกันทั้งนั้น
น่าสนใจที่ต้องให้ แอป React ของ ChatGPT เรนเดอร์เสร็จสมบูรณ์ ก่อน จึงจะมีพร็อพเพอร์ตีบางอย่างปรากฏ
นี่เป็นวิธีตรวจจับบอตในระดับ แอปพลิเคชันเลเยอร์ ไม่ใช่ระดับเบราว์เซอร์
ผมนึกว่าระบบตรวจจับขั้นสูงส่วนใหญ่ก็ทำงานแบบนี้อยู่แล้ว เลยสงสัยว่าการค้นพบครั้งนี้มีความหมายพิเศษอะไรหรือไม่
มีคนอยากให้ผู้เขียนอธิบายให้ชัดกว่านี้ว่าทำไมประเด็นนี้ถึงสำคัญ
สุดท้ายแล้ว OpenAI ก็แค่ต้องการให้ผู้ใช้ ใช้แอป React อย่างเป็นทางการ และยังไม่ค่อยเห็นว่านั่นเป็นปัญหาตรงไหน
น่าสนใจในเชิงเทคนิค
เท่าที่รู้ Turnstile ปกติไม่ได้มีการตั้งค่ารายเว็บไซต์ลึกขนาดนั้น เลยสงสัยว่า OpenAI เอา ข้อมูลจาก Turnstile มาผูกกับ API ของตัวเอง ได้อย่างไร
มีความเห็นว่าไม่เข้าใจว่าทำไมคนทำบอตไม่แค่รัน Windows 11 VM + Chrome ไปเลย
ถ้าใช้ memory deduplication ก็รัน VM พร้อมกันได้ 50 ตัว และบน AWS ต้นทุนก็แค่ราว 1 เซนต์ต่อการโหลดหน้า 1,000 ครั้ง ซึ่งถูกมาก
มีทั้งการหมุน IP ปลอมตำแหน่ง ปรับค่าภาษา มี parser ในตัว ฯลฯ และต้นทุนในการเปลี่ยนไปใช้ก็ต่ำ จึงแทบไม่มีเหตุผลต้องสร้างเอง
เพราะแพงกว่าวิธีง่าย ๆ มาก
และถ้าใช้ Linux container แทน Windows ตามที่เสนอ ก็จะเบากว่าและมีประสิทธิภาพมากกว่า
มีคำวิจารณ์ว่าเป็น บทความหยาบ ๆ เหมือน ChatGPT เขียน
มีคนเคยใช้ส่วนขยายชื่อ KeepChatGPT ในช่วงปี 2023~2024
วิธีที่มันทำงานโดยสวมรอยเป็นผู้ใช้โดยไม่ต้องใช้ API นั้นน่าสนใจดี
ต่อมาหยุดใช้หลังจาก Gemini ปรากฏตัว และเกิดข้อผิดพลาดบ่อย แต่ก็ชอบตัวเลือกที่ย้ายแผง AI ไปไว้ด้านขวา
ลิงก์ GitHub ของ KeepChatGPT
วิธีนี้ ไม่ติดระบบแอนติบอตของ OpenAI เลย
มีคนถามว่าการเชื่อมรวมกันระหว่าง Cloudflare กับแอปนี้เป็น ฟังก์ชันคัสตอมที่เกินกว่า Turnstile มาตรฐาน หรือเป็นฟีเจอร์เฉพาะระดับ enterprise กันแน่