สรุปสั้น

งานวิจัยใหม่ชี้ว่าแฮช (hash) ของ Git commit ที่เซ็นชื่อดิจิทัลแล้ว ไม่ได้เป็น “ชื่อเฉพาะที่ไม่ซ้ำใคร” อย่างที่แวดวงซอฟต์แวร์จำนวนมากเข้าใจกัน โดยเมื่อมี commit ที่เซ็นชื่อไว้แล้วหนึ่งอัน ผู้ที่ไม่มีคีย์สำหรับเซ็นชื่อก็ยังสามารถสร้าง commit อันที่สองซึ่งมีไฟล์ ผู้เขียน และวันที่เหมือนกันทุกอย่าง พร้อมลายเซ็นที่ถูกต้อง และ GitHub ก็ยังขึ้นตรา “Verified” ให้ ทุกอย่างที่ผู้ตรวจสอบโค้ดจะดูล้วนตรงกันหมด ยกเว้นแฮชของ commit ที่ต่างออกไป

เรื่องนี้สำคัญเพราะระบบจำนวนมากปฏิบัติต่อแฮชของ commit ที่ verified แล้วเสมือนเป็นชื่อถาวรที่ไม่ซ้ำใครของเนื้อหานั้น ผลลัพธ์ที่เป็นรูปธรรมคือ หากองค์กรบล็อก commit ที่เป็นอันตรายด้วยการระบุค่าแฮช ผู้โจมตีก็สามารถ push เนื้อหาเดิมซ้ำภายใต้แฮชใหม่ที่ยังขึ้น “Verified” และ blocklist ไม่เคยเห็นมาก่อนได้ ระบบที่ทำ deduplication, บันทึกที่มา (provenance log) และบันทึก reproducible build ที่ยึดค่าแฮชเป็นหลักล้วนได้รับผลกระทบเช่นกัน ทั้งนี้จุดอ่อนนี้ไม่ใช่ช่องทางแอบสอดโค้ดที่ต่างออกไปให้ผ่านการตรวจลายเซ็น เพราะไฟล์ในทุกสำเนายังเหมือนกันทุกประการ งานวิจัยนี้ไม่มีการออกรหัส CVE และไม่มีประกาศเตือนจากผู้ผลิต เพราะจุดอ่อนอยู่ที่วิธีที่แพลตฟอร์มโฮสต์โค้ด (forge) ตัดสินความหมายของคำว่า “Verified” และการแก้ไขต้องทำที่ฝั่ง forge เอง

รายละเอียดข่าว

เว็บไซต์ The Hacker News รายงานเมื่อวันที่ 8 กรกฎาคม 2569 ว่า งานวิจัยชิ้นใหม่จาก นาย Jacob Ginesin นักศึกษาปริญญาเอกจากมหาวิทยาลัย Carnegie Mellon และผู้ตรวจสอบด้านการเข้ารหัส (cryptographic auditor) ของบริษัท Cure53 ได้เปิดเผยว่าแฮชของ Git commit ที่เซ็นชื่อแล้วสามารถถูกเขียนใหม่ให้มีค่าต่างออกไปได้ โดยที่ลายเซ็นยังคงถูกต้องและ GitHub ยังขึ้นตรา Verified

งานวิจัยดังกล่าวเป็นเอกสารความยาว 5 หน้าที่เผยแพร่บน arXiv เมื่อวันที่ 2 กรกฎาคม พร้อมเครื่องมือสาธารณะที่รันการโจมตีทั้งสามรูปแบบ และคลัง repository สาธิตสองแห่งที่ commit ซึ่งถูกดัดแปลงยังคงแสดงสถานะ “Verified” บน GitHub นาย Ginesin เรียกผลกระทบนี้ว่า “hash chain malleability” โดยอธิบายว่าเนื่องจาก commit ทุกอันอ้างถึง commit แม่ (parent) ด้วยค่าแฮช การดัดแปลง commit หนึ่งจึงบังคับให้ commit ที่อยู่เหนือขึ้นไปต้องมีแฮชใหม่ตามไปด้วย ซึ่งเครื่องมือของเขาจะเขียนสายโซ่นี้ใหม่ให้สอดคล้องกัน อย่างไรก็ตาม commit ลูกที่เซ็นชื่อไว้จะเสียตรา Verified ของตัวเองทันทีที่ตัวชี้ parent เปลี่ยนไป

นาย Ginesin ระบุว่าได้รายงานปัญหานี้ไปยัง GNU และ Git ตั้งแต่เดือนมกราคม และแจ้ง GitHub เมื่อเดือนมีนาคม แต่ ณ เวลาที่เผยแพร่งานวิจัย ทั้ง Git และ forge รายใดยังไม่ได้แก้ไข โดยจุดเริ่มต้นที่ชัดเจนที่สุดในการแก้คือกรณี S/MIME ที่ GitHub ยังยอมรับสิ่งที่การตรวจสอบในเครื่องแบบเข้มงวดปฏิเสธ

รายละเอียดทางเทคนิค

ต้นตอของปัญหาคือ signature malleability (ความยืดหยุ่นของลายเซ็น) เนื่องจากค่าแฮชของ commit ถูกคำนวณจากทุกอย่างที่อยู่ภายใน รวมถึงไบต์ดิบของลายเซ็นในส่วนหัวด้วย ลายเซ็นจำนวนมากสามารถถูกเขียนใหม่ให้อยู่ในรูปแบบที่ต่างออกไปแต่ยังถูกต้อง การเปลี่ยนไบต์เหล่านั้นจึงเปลี่ยนค่าแฮชโดยไม่ต้องแตะโค้ดแม้แต่บรรทัดเดียว งานวิจัยนำเสนอสามเส้นทางที่ครอบคลุมทุกรูปแบบลายเซ็น GPG ที่ GitHub ตรวจสอบ รวมถึง S/MIME

เส้นทางแรกคือคีย์ ECDSA ที่ใช้พีชคณิตบนเส้นโค้งวงรีแบบคลาสสิกพลิกค่าลายเซ็น (เปลี่ยนค่า s เป็น n − s) ซึ่งทั้งสองรูปแบบถูกต้อง ผ่านทั้งการตรวจ git verify-commit ในเครื่องและได้ตรา Verified บน GitHub เส้นทางที่สองคือคีย์ RSA และ EdDSA ที่เพิ่มฟิลด์พิเศษซึ่งถูกละเลยเข้าไปในส่วน “unhashed” ของลายเซ็น ซึ่งเป็นส่วนที่ลายเซ็นจงใจไม่ครอบคลุม ทำให้ลายเซ็นยังผ่านการตรวจแต่ไบต์และแฮชของ commit เปลี่ยนไป และเส้นทางที่สามคือคีย์ S/MIME (X.509) ที่เขียนฟิลด์ระบุความยาวในโครงสร้าง DER ของลายเซ็นให้ยาวขึ้นในรูปแบบที่ไม่เป็นมาตรฐาน ซึ่งการตรวจในเครื่องแบบเข้มงวด (ผ่าน gpgsm) จะปฏิเสธ แต่ GitHub ยังคงขึ้น Verified

ตัวการร่วมของทั้งสามเส้นทางคือ GitHub ไม่ได้ทำ normalize ลายเซ็นก่อนตรวจสอบ กล่าวคือไม่บังคับการเข้ารหัสแบบเข้มงวดกับ S/MIME ไม่ตัดฟิลด์ OpenPGP ส่วนเกินออก และยอมรับค่า ECDSA ที่ไม่เป็น canonical ตามที่ส่งมา จากนั้น GitHub จะบันทึกสถานะ “Verified” ไว้กับค่าแฮชแต่ละอันและไม่ตรวจซ้ำอีก ทำให้ commit ยังคงสถานะ Verified แม้คีย์ที่ใช้เซ็นจะถูกเพิกถอนไปแล้ว และหาก push commit ต้นฉบับกับฝาแฝดไปคนละ branch หน้าเปรียบเทียบของ GitHub จะมองว่าเป็นประวัติที่แตกต่างกัน (นำหน้าหนึ่ง commit และตามหลังหนึ่ง commit) ทั้งที่ไฟล์เหมือนกันทุกประการ ทั้งนี้งานวิจัยย้ำว่านี่ไม่ใช่การชนกันของแฮช (hash collision) ไม่ได้ทำลาย SHA-1 หรือ SHA-256 แต่เป็นสิ่งตรงกันข้าม คือ commit หนึ่งอันสามารถเขียนได้หลายรูปแบบที่ถูกต้อง โดยแต่ละรูปแบบมีแฮชของตัวเอง แนวคิดนี้เคยเกิดกับ Bitcoin ที่เผชิญความสมมาตรของ ECDSA แบบเดียวกันมาก่อน และแก้ด้วยการยอมรับเฉพาะรูปแบบ “low-S” แล้วต่อมาย้ายลายเซ็นออกจาก ID ด้วย SegWit

ผลกระทบต่อไทย

นักพัฒนาและองค์กรในไทยที่ใช้ GitHub และพึ่งพาตรา Verified เป็นเครื่องยืนยันความน่าเชื่อถือของ commit ควรเข้าใจว่าตรานี้ยืนยันได้เพียงว่าใครเป็นผู้เซ็น commit เท่านั้น แต่ไม่ได้ทำให้ค่าแฮชของ commit เป็นชื่อเฉพาะที่ไม่ซ้ำของเนื้อหา ประเด็นนี้สำคัญเป็นพิเศษกับทีมที่วางระบบความปลอดภัยของห่วงโซ่อุปทานซอฟต์แวร์ (supply chain) โดยใช้ blocklist ตามค่าแฮช ระบบบันทึกที่มา หรือ reproducible build เพราะกลไกเหล่านี้อาจถูกหลบเลี่ยงได้ อย่างไรก็ตาม ข่าวดีคือนักพัฒนาทั่วไปที่ pin การใช้งาน GitHub Action หรือโมดูลด้วยค่าแฮชเต็มไม่จำเป็นต้องเปลี่ยนอะไร เพราะแฮชที่ pin ไว้ยังคงดึงโค้ดที่ถูกต้องมาได้เสมอ งานวิจัยนี้จึงเป็นเครื่องเตือนว่าอย่าพึ่งพาการมีลายเซ็นที่ถูกต้องเพียงอย่างเดียวเป็นสัญญาณชี้ว่า commit ปลอดภัย

คำแนะนำ

ภาระในการแก้ไขอยู่ที่ผู้ให้บริการแพลตฟอร์มโฮสต์โค้ด (forge) ไม่ใช่นักพัฒนา โดยงานวิจัยแนะนำให้ forge ทำ canonicalize ลายเซ็นให้อยู่ในรูปแบบมาตรฐานก่อนที่จะเชื่อถือค่าแฮช สำหรับองค์กรทั่วไป ควรยังคงแนวปฏิบัติเดิมคือ pin การใช้งาน GitHub Action และ dependency ด้วยค่าแฮช commit เต็ม แทนการอ้างอิงแท็กที่เลื่อนได้ ซึ่งเป็นคำแนะนำที่ยังใช้ได้ผลหลังเหตุการณ์โจมตี tj-actions/changed-files ในปี 2568 และ trivy-action ในปี 2569 นอกจากนี้ เครื่องมือใด ๆ ที่บล็อก ทำ deduplication หรือบันทึกที่มาโดยยึดค่าแฮชของ commit ควรตรวจสอบและ canonicalize ลายเซ็นก่อน แทนการเชื่อค่าแฮชดิบของอ็อบเจกต์ที่เซ็นชื่อซึ่งผู้โจมตีสามารถเข้ารหัสใหม่ได้ ทั้งนี้ระบบที่ pin ค่าแฮชของไฟล์ที่ดึงมาแยกต่างหากด้วย เช่น fixed-output derivations ของ Nix จะยังมีแนวป้องกันสำรองเหลืออยู่

แหล่งอ้างอิง