- GitHub Actions มีโครงสร้างที่ประกาศและรัน การพึ่งพาแพ็กเกจ ผ่านรูปแบบ
uses: ในไฟล์ workflow ซึ่งในทางปฏิบัติทำหน้าที่เป็นตัวจัดการแพ็กเกจ
- อย่างไรก็ตาม ไม่มี lockfile, hash ความสมบูรณ์, การ pin การพึ่งพาแบบทรานซิทีฟ, และความสามารถในการมองเห็นต้นไม้การพึ่งพา ซึ่งเป็นฟีเจอร์หลักที่ตัวจัดการแพ็กเกจอื่นมอบให้เป็นค่าเริ่มต้น
- จากการวิจัยพบว่า ผู้ใช้ GitHub Actions ส่วนใหญ่รันโค้ดจากบุคคลภายนอกที่ไม่ได้รับการตรวจสอบ และมีช่องโหว่การฉีดโค้ดปรากฏใน workflow จำนวนหลายพันรายการ
- GitHub มีมาตรการบรรเทาบางส่วน (เช่น immutable release, นโยบาย pin ด้วย SHA ฯลฯ) แต่ ปัญหาการพึ่งพาแบบทรานซิทีฟและความสามารถในการทำซ้ำยังคงไม่ถูกแก้ไข
- ข้อบกพร่องเชิงโครงสร้างเหล่านี้ส่งผลกระทบต่อความมั่นคงปลอดภัยของซัพพลายเชนซอฟต์แวร์ทั้งหมด และถูกขยายไปยังระบบ CI อื่น ๆ ที่อาศัย GitHub Actions
โครงสร้างการจัดการแพ็กเกจของ GitHub Actions และปัญหา
- ไวยากรณ์เช่น
uses: actions/checkout@v4 คือการ ประกาศ dependency และ GitHub แปลผลเพื่อต้องดาวน์โหลดและรัน
- เป็นพฤติกรรมใกล้เคียงกับการจัดการแพ็กเกจทั่วไป แต่เนื่องจาก ไม่มี lockfile ทำให้แต่ละครั้งที่รันอาจเลือกเวอร์ชันแตกต่างกัน
- เมื่อเทียบกับตัวจัดการแพ็กเกจอื่น ๆ (npm, Cargo, NuGet เป็นต้น) Actions ขาด lockfile, การ pin dependency แบบทรานซิทีฟ, การตรวจสอบความสมบูรณ์, ความสามารถในการมองเห็นต้นไม้ dependency และสเปกการ resolve ทั้งหมด
- การไม่มี lockfile เป็นปัญหาหลัก ทำให้การตีความ dependency เปลี่ยนไปทุกครั้งที่รันและเกิด การ build แบบไม่กำหนดผลได้ (non-deterministic build)
ผลการวิจัยด้านความปลอดภัยและช่องโหว่
- การวิจัย USENIX Security 2022 ระบุว่า 99.7% ของ repository รัน Actions จากนักพัฒนาภายนอก และ 97% มาจากผู้สร้างที่ไม่ได้รับการยืนยัน และ 18% ไม่มีการอัปเดตความปลอดภัย
- งานวิจัยต่อมาพบช่องโหว่การฉีดโค้ดใน มากกว่า 4,300 ของ workflow จาก 2.7 ล้าน รายการ
- GitHub Actions ยังไม่จัดให้ครบคุณสมบัติความปลอดภัยที่จำเป็นของ CI/CD เช่น
admittance control, execution control, code control, และการควบคุมการเข้าถึง secrets
ข้อบกพร่องทางเทคนิคสำคัญ
- ปัญหาเวอร์ชันแบบเปลี่ยนได้: แท็กอย่าง
@v4 สามารถถูก re-tag ด้วย commit ใหม่โดยผู้ดูแลได้ ทำให้โค้ดถูก เปลี่ยนแปลงอย่างเงียบ
- หากมี lockfile ก็สามารถบันทึกได้ว่าแท็กนั้นถูกตีความเป็น SHA ใด
- ความไม่โปร่งใสของการพึ่งพาแบบทรานซิทีฟ: Action ที่เป็น Composite เรียกใช้ Action อื่นจากภายในไม่สามารถมองเห็นหรือตั้งการควบคุมได้
- การวิจัยพบว่า JavaScript Actions จำนวน 54% มีจุดอ่อนด้านความปลอดภัย และส่วนใหญ่เกิดจาก dependency แบบทางอ้อม
- ในกรณี
tj-actions/changed-files มีการโจมตีรั่วไหลข้อมูลลับผ่านการอัปเดต dependency แบบทรานซิทีฟ
- ขาดการตรวจสอบความสมบูรณ์: npm หรือ Cargo บันทึก hash เพื่อยืนยันการดาวน์โหลด แต่ Actions พึ่งพาความน่าเชื่อถือจาก SHA เท่านั้น
- การรันซ้ำไม่สามารถทำซ้ำได้: GitHub ระบุว่าหากมีการ forced push เวอร์ชันล่าสุดจะถูกดึงมาใช้งานเสมอ ดังนั้น แม้ workflow เดิมก็อาจรันโค้ดที่ต่างกันได้
- การมองไม่เห็นต้นไม้ dependency: ไม่มีฟังก์ชันแบบ
npm ls ของ npm หรือ cargo tree ของ Cargo ทำให้ไม่มีวิธีตรวจสอบโครงสร้าง dependency ทั้งหมด
- กฎการ resolve ไม่เปิดเผย: การ resolve dependency ของ Actions ไม่ได้มีเอกสารอธิบาย และในโค้ด
ActionManager.cs มีเพียง logic ดาวน์โหลดแบบ recursive อย่างง่าย
ข้อจำกัดเชิงโครงสร้างเพิ่มเติม
- ไม่มี registry กลาง: Actions อยู่ใน Git repository และยังไม่มีความสามารถในการสแกนความปลอดภัย, ตรวจจับมัลแวร์, หรือป้องกัน typosquatting
- ปัญหาสภาพแวดล้อมที่แชร์กัน: Action หลายตัวแก้ไข
$PATH เดียวกัน ทำให้ผลลัพธ์ต่างกันตามลำดับการรัน
- ไม่สามารถรันแบบออฟไลน์ได้: ทุกครั้งต้องดาวน์โหลดจาก GitHub จึงไม่สามารถรันได้หากไม่มีเครือข่าย
- ช่องโหว่ที่ namespace: ชื่อผู้ใช้ GitHub ทำหน้าที่เป็น namespace โดยตรง ทำให้เสี่ยงต่อการแฮกบัญชีหรือการโจมตีแบบพิมพ์ผิด
- หากมี lockfile และ hash ความสมบูรณ์ การเปลี่ยนโค้ดจะถูกตรวจพบผ่านความล้มเหลวในการ build
ที่มาของการออกแบบและผลกระทบ
- Runner ของ Actions เดิมมีรากฐานจาก Azure DevOps และถูกออกแบบโดยสมมติฐานว่าอยู่ในสภาพแวดล้อมที่เชื่อถือได้
- เมื่อ GitHub ขยายไปเป็น marketplace สาธารณะ จึงยังไม่ได้ออกแบบใหม่โมเดลความไว้วางใจใหม่
- จึงทำให้ขาดฟีเจอร์พื้นฐานอย่าง lockfile, การตรวจสอบความสมบูรณ์, การ pin แบบทรานซิทีฟ, ความโปร่งใสของ dependency
- เมื่อ OIDC-based การ deploy แพ็กเกจอัตโนมัติ กำลังแพร่หลาย ความเสี่ยงของ Actions ก็ส่งผลต่อความปลอดภัยซัพพลายเชนของ registry แพ็กเกจทั้งหมด
- GitLab CI นำคีย์
integrity มาใช้เพื่อตรวจสอบ hash แต่ GitHub ปฏิเสธคำขอเดียวกันว่าไม่มีแผน
- ระบบ CI ที่รองรับ GitHub อย่าง Forgejo Actions ก็จำเป็นต้องคงโครงสร้างเดียวกัน ทำให้ข้อบกพร่องแพร่ไปทั่ว ecosystem
ข้อเสนอแนะการแก้ไขและสถานะปัจจุบัน
- ชุมชนขอให้มีการรองรับ lockfile (issue #2195) แต่ GitHub ปิดข้อเสนอในปี 2022 ด้วยข้อความว่า “No plan”
- การศึกษาโดย Palo Alto ยืนยันว่า การ pin เฉพาะ SHA อย่างเดียวไม่สามารถป้องกัน dependency แบบทรานซิทีฟได้
- บางทีมเสริมด้วย การอัปเดตผ่าน Dependabot, vendor repository เอง, ตัวสแกนความปลอดภัย zizmor ฯลฯ
- แนวทางแก้ไขเชิงโครงสร้างที่แท้จริงคือต้องนำ lockfile เข้ามาบันทึก SHA และ hash ความสมบูรณ์ของทุก Action และ dependency แบบทรานซิทีฟ
- หาก GitHub ไม่ยอมรับแนวทางนี้ ความน่าเชื่อถือของซัพพลายเชน CI/CD ก็ไม่สามารถทำให้มั่นใจได้อย่างแท้จริง
2 ความคิดเห็น
คงต้องโดนแฮ็กก่อนถึงจะได้สติสินะ
ความเห็นบน Hacker News
ไม่ได้อยากปกป้อง GHA (GitHub Actions) แต่ในเอกสารก็ระบุชัดว่าควร ตรึง commit SHA เพื่อความเสถียรและความปลอดภัย
ถึงจะทำเองแบบไฟล์ lock ได้โดยตรง แต่ก็ไม่สมบูรณ์เพราะควบคุม transitive dependency ไม่ได้
สุดท้ายก็ต้องแบกรับภาระในการติดตาม security patch และอัปเดตแฮช จึงดูเหมือนว่านี่เป็นเหตุผลที่การตรึงแบบอิงแฮชไม่ค่อยถูกใช้อย่างแพร่หลาย
ผู้ใช้ส่วนใหญ่ไม่รู้ว่ามีปัญหา และแม้แต่คนที่รู้ก็แทบไม่ใช้ SHA
ส่วนตัวฉันชอบ Actions และดูแลบางตัวอยู่ แต่พอดู public repository แล้วประมาณ 90% ใช้
@v1, 9% ใช้@v1.2และมีแค่ 1% ที่ใช้ commit SHAถ้า GitHub ลงทุนอีกนิดก็น่าจะสร้าง โซลูชันแบบ lock file ได้
บ่อยครั้งมันขึ้นกับ Node เวอร์ชันหรือ API เวอร์ชันบางตัว จนมีประสบการณ์ว่าการใช้ @main กลับดีกว่าเสียอีก
มันทำให้เข้าใจผิดว่าได้ “เวอร์ชันที่ตรึงไว้” แต่จริง ๆ แล้วไม่ใช่
เจอปัญหามาสองครั้งถึงได้ตระหนักว่า มันมีแค่สองแบบคือมี lock file หรือไม่มี
GitHub แทบจะ ไม่ดูแลรักษา Actions ของตัวเองแล้ว จนแม้แต่ฟีเจอร์พื้นฐานก็ต้องพึ่ง ฟอร์กไม่เป็นทางการ
ให้ความรู้สึกว่า ecosystem ทั้งหมดกำลังอยู่ได้ด้วยการแก้ขัดไปวัน ๆ
เพราะ GitHub ประกาศว่าจะให้ความสำคัญกับ การย้ายระบบไป Azure มากกว่าการพัฒนาฟีเจอร์
บทความที่เกี่ยวข้อง
แม้แต่บริษัทเล็ก ๆ ของเราก็ยังจ่ายเกิน 200 ดอลลาร์ต่อเดือน
ดูเหมือนว่าจะถูกมองเป็น แหล่งรายได้ใหม่ ที่สำคัญยิ่งกว่า Windows เสียอีก
คนเขียนดั้งเดิมคงลาออกจากบริษัทไปแล้ว
สงสัยว่ามีใครเคยใช้ ArgoCD เป็น CI pipeline บ้างไหม
ฉันคิดว่า GHA เป็นตัวอย่างความล้มเหลวของปรัชญา ‘less is more’
ปัญหาใหญ่ที่สุดคือมันกลายเป็นมาตรฐานของวงการไปแล้ว
ถ้าลงทุนอีกนิดก็ทำ CI ที่ดีกว่านี้ได้มาก แต่ให้ความรู้สึกว่า MS กำลังทำผิดซ้ำแบบ ยุค IE6
ตอนนี้วิศวกรรุ่นใหม่ที่ไม่มีประสบการณ์เปรียบเทียบกลับไม่ทันตระหนักถึงข้อจำกัดของมัน
ฉันกำลังคิดจะเอาโน้ตบุ๊กที่ปลดระวางแล้วมารันเป็นเซิร์ฟเวอร์ Woodpecker อยากรู้ว่า CI ที่คนไม่เกลียดเป็นอย่างไร
เมื่อเทียบกันแล้ว GHA ดูไม่มีคุณค่าอะไรเป็นพิเศษ
ตอนที่บริษัทพยายามย้ายจาก Jenkins/Ansible ไป GHA ฉันคัดค้านไว้ และตอนนี้ดูเหมือนว่าจะเป็นการตัดสินใจที่ถูกแล้ว
CI มี ภาระในการดูแลรักษา สูงอยู่เสมอ และโดยเฉพาะบน Mac ก็ยังจัดการยากเหมือนเดิม
สำหรับคำถามว่า “เชื่อหรือไม่ว่า GitHub จะให้โค้ด SHA ที่ถูกต้อง”
เมื่อมองจากความจริงที่ว่าคนส่วนใหญ่ใช้ GitHub-hosted runner กันอยู่แล้ว ถ้าเชื่อเรื่องนั้นไม่ได้ ก็คงมีปัญหาใหญ่กว่านั้นอยู่ก่อนแล้ว
ทำให้นึกสงสัยว่า ถ้า GitHub Actions เป็นสถาปัตยกรรมแบบ local-first และรองรับ การล็อกบนพื้นฐานของ Nix จะเป็นอย่างไร
cachix/cloud.devenv.sh
หลาย ๆ third-party Actions อ้างถึง สาขา master โดยตรงในเอกสารหรือในตัวอย่าง
แค่มีการ push ที่เป็นอันตรายครั้งเดียว ก็อาจทำให้เกิด ข้อมูลรั่วไหล ได้ใน repository จำนวนมาก
ต่อให้ใช้ tag ก็ยังขยับได้ จึงไม่ใช่การป้องกันที่สมบูรณ์
พอเห็นที่นักวิจัยพูดถึงคุณสมบัติความปลอดภัยหลัก 4 ประการของ CI/CD (authentication, execution, code, secret access) ก็เกิดคำถามขึ้นมา
CI/CD จำเป็นต้องเข้าถึง secrets จริงหรือ?
ฉันคิดว่าแค่มีสิทธิ์เรียก API ก็น่าจะพอแล้ว
ระบบในอุดมคติไม่ควรจัดการ secrets โดยตรง แต่ควรจัดการทางอ้อมผ่านตัวปรับแบบ secure enclave หรืออะไรทำนองนั้น
ในโลกจริง ลูกค้าส่วนใหญ่ก็ยังต้องใช้ secrets อยู่ดี
อย่างน้อยแพลตฟอร์มก็ควรมี กลไกจัดการ secrets ที่ปลอดภัย ให้
โดยเฉพาะโปรเจกต์โอเพนซอร์สที่ต้องการ deploy โดยตรงจาก CI
เลยสงสัยว่า “แนวทาง secure enclave” นั้นต่างออกไปอย่างไรในทางปฏิบัติ
แต่ในความเป็นจริงแต่ละสภาพแวดล้อมต่างกัน และต้นทุนการทำระบบก็สูง สุดท้ายคนส่วนใหญ่เลยจบที่วิธี container + environment variables
ถ้าจะทำการทดสอบแบบนี้ให้อัตโนมัติ การมี secrets ก็หลีกเลี่ยงไม่ได้
ถ้าต้อง commit lock file เข้า repository ก็จะเกิด ปัญหา bootstrapping ตอนสร้างครั้งแรก
ถ้าจะแก้เรื่องนี้ก็ควรมีความสามารถในการ รัน Actions บนเครื่องโลคัล ได้
มีเครื่องมืออย่าง nektos/act แต่หลัก ๆ เอาไว้ใช้ดีบัก
อาจจำเป็นต้องมีกลไกแยกต่างหากที่อิงกับ static analysis