ทำความรู้จักกับ GitHub Actions และภัยเงียบจากโค้ดภายนอก
เวลาที่เราเขียนโปรแกรมและเอาโค้ดไปเก็บไว้บน GitHub (เว็บไซต์สำหรับเก็บและจัดการซอร์สโค้ด) เรามักจะใช้ฟีเจอร์ที่เรียกว่า GitHub Actions (ระบบช่วยรันคำสั่งอัตโนมัติ เช่น การทดสอบโค้ดหรือการนำโค้ดขึ้นเซิร์ฟเวอร์) เพื่อทุ่นแรงให้เราไม่ต้องมานั่งทำทุกอย่างด้วยตัวเองซ้ำๆ ทุกครั้งที่อัปเดตโค้ดใหม่
ลองจินตนาการว่า GitHub Actions เหมือนกับลูกมือที่เราจ้างมาช่วยงานในโรงงาน ซึ่งลูกมือเหล่านี้มักจะเป็นโค้ดที่คนอื่นเขียนไว้ให้เราหยิบมาใช้ได้ฟรีๆ แต่นี่แหละคือจุดเสี่ยง เพราะถ้าวันหนึ่งลูกมือคนนี้ถูกเปลี่ยนตัวหรือถูกผู้ไม่หวังดีแอบเข้ามาแก้โค้ด งานที่เราไว้ใจให้เขารันก็อาจจะมีคำสั่งอันตรายแฝงมาด้วยโดยที่เราไม่รู้ตัว
ภัยนี้เราเรียกว่าการทำ Poisoned Dependencies (การใส่โค้ดอันตรายเข้าไปในส่วนประกอบที่เรานำมาใช้งาน) ซึ่งเปรียบเหมือนการที่เราไปซื้ออะไหล่รถยนต์จากร้านที่ดูน่าเชื่อถือ แต่ดันมีคนแอบเอาอะไหล่ปลอมที่พังง่ายไปวางปนไว้ ทำให้รถเราขับไปแล้วเกิดอุบัติเหตุกลางทางนั่นเอง
หากเราเป็นโปรแกรมเมอร์มือใหม่ การเข้าใจเรื่องนี้จะช่วยให้เราสร้างระบบที่ปลอดภัยตั้งแต่ต้น ไม่ใช่แค่เขียนโค้ดให้รันได้ แต่ต้องเขียนให้รันได้อย่างปลอดภัยด้วย เรามาดูกันว่าเราจะป้องกันปัญหานี้ได้อย่างไรบ้าง
ทำไมการใช้เลขเวอร์ชันแบบลอยตัวถึงอันตราย
ในการใช้งาน GitHub Actions หลายคนมักจะเขียนอ้างอิงถึงโค้ดคนอื่นด้วยเลขเวอร์ชัน เช่น @v4 ซึ่งเปรียบเหมือนการบอกลูกมือว่า "ไปหยิบอุปกรณ์รุ่น 4 มาให้หน่อย" โดยที่ระบบจะไปดึงโค้ดเวอร์ชันล่าสุดที่ถูกแปะป้ายชื่อว่า v4 มาใช้งานให้เราโดยอัตโนมัติ
ปัญหาคือป้ายชื่อเหล่านี้มัน เคลื่อนที่ได้ (Floating Tags) เพราะเจ้าของโค้ดสามารถสั่งเปลี่ยนได้ตลอดเวลาว่า v4 จะให้ชี้ไปที่โค้ดตัวไหน หากวันหนึ่งบัญชีของผู้พัฒนาคนนั้นถูกแฮ็กหรือถูกเปลี่ยนตัว คนร้ายก็สามารถแก้ไขโค้ดที่อยู่หลังป้าย v4 ให้กลายเป็นโค้ดขโมยข้อมูลได้ทันทีโดยที่เราไม่ต้องทำอะไรเลย
เรามาดูตัวอย่างการเขียนแบบที่เสี่ยงอันตรายกันครับ:
# ตัวอย่างการเขียนแบบเสี่ยงอันตราย
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # อันตราย เพราะ v4 เปลี่ยนโค้ดข้างในได้ตลอด
- uses: actions/setup-node@v4 # อันตราย เพราะ v4 อาจถูกแอบแก้ได้ในอนาคต
คำอธิบายโค้ด:
usesคือคำสั่งบอกให้ไปดึงโค้ดคนอื่นมาใช้งานactions/checkout@v4คือการบอกให้ใช้โค้ดชุดสำหรับดึงไฟล์จาก Repository (ที่เก็บโค้ดของโปรเจกต์) โดยใช้เวอร์ชัน 4- หากมีการอัปเดตเวอร์ชัน 4 ให้เป็นโค้ดใหม่ ระบบของเราจะรันโค้ดใหม่นั้นทันทีโดยไม่ได้ตรวจสอบก่อน
ผลลัพธ์ที่เกิดขึ้น: ทุกครั้งที่คุณสั่งรัน Workflow (ขั้นตอนการทำงานอัตโนมัติ) ระบบจะไปดึงเวอร์ชันล่าสุดของ v4 มาใช้เสมอ ซึ่งถ้าเจ้าของโปรเจกต์แอบใส่โค้ดขโมยรหัสผ่านเข้าไป คุณก็จะโดนขโมยไปโดยไม่รู้ตัว
วิธีป้องกันด้วยการล็อกเลขรหัส Commit SHA
วิธีแก้ปัญหาที่ง่ายและได้ผลดีที่สุด คือการใช้ Commit SHA (รหัสตัวเลขและตัวอักษร 40 หลักที่ระบุเวอร์ชันของโค้ดแบบถาวร) แทนที่จะใช้เลขเวอร์ชันแบบลอยตัว การใช้รหัสนี้เปรียบเหมือนการระบุเลขบัตรประชาชนของโค้ดตัวนั้นๆ ลงไปเลย ไม่ว่าใครจะพยายามเปลี่ยนป้ายชื่อเวอร์ชันยังไง โค้ดของเราก็จะหยิบเฉพาะตัวเดิมที่เราเคยตรวจสอบแล้วมาใช้งานเสมอ
การทำแบบนี้จะทำให้ Workflow ของเรามีความคงที่ (Immutable) คือไม่มีวันเปลี่ยนเองจนกว่าเราจะเป็นคนเปลี่ยนมันด้วยตัวเอง นี่คือมาตรฐานความปลอดภัยที่มืออาชีพทุกคนต้องทำในการเขียน CI (ขั้นตอนการตรวจสอบโค้ดอัตโนมัติ)
เรามาดูตัวอย่างการเขียนแบบที่ปลอดภัยกันครับ:
# ตัวอย่างการเขียนแบบปลอดภัย
jobs:
build:
runs-on: ubuntu-latest
steps:
# ล็อกด้วยรหัส SHA 40 หลัก เพื่อความปลอดภัยสูงสุด
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@4992456781334f2795ae9dd169f5041797d85689 # v4.4.0
คำอธิบายโค้ด:
- รหัสยาวๆ เช่น
11bd7190...คือ Commit SHA ที่ระบุตัวตนของโค้ดเวอร์ชันนั้นๆ อย่างชัดเจน - เรายังใส่คอมเมนต์
# v4.2.2ไว้ข้างหลังเพื่อให้มนุษย์อ่านเข้าใจง่าย แต่ตัวระบบจะรันตามรหัส SHA เท่านั้น - หากมีการแอบแก้โค้ดใน
v4.2.2รหัส SHA นี้จะไม่ตรงกับของใหม่ และระบบจะไม่รันโค้ดที่ผิดปกติให้เรา
ผลลัพธ์ที่เกิดขึ้น: ระบบจะดึงโค้ดเวอร์ชันที่ระบุไว้เป๊ะๆ มาใช้งาน หากโค้ดต้นทางถูกแก้ไขจนรหัสไม่ตรงกัน GitHub จะหยุดทำงานหรือแจ้งเตือนทันที ช่วยป้องกันการถูกแอบเปลี่ยนโค้ดได้ 100%
ใช้งาน Dependabot เพื่ออัปเดตอย่างมั่นใจ
เมื่อเราล็อกโค้ดด้วยรหัส SHA แล้ว เราก็ต้องคอยอัปเดตเป็นระยะเพื่อให้ได้ฟีเจอร์ใหม่หรือแก้ไขบั๊ก แต่การนั่งไล่เปลี่ยนรหัสเองทุกครั้งก็เป็นเรื่องที่น่าปวดหัวและเสี่ยงต่อการลืม นี่คือหน้าที่ของ Dependabot (บอทของ GitHub ที่คอยตรวจสอบและอัปเดตโปรแกรมให้เรา)
เราสามารถตั้งค่าให้ Dependabot คอยตรวจดูว่ามีเวอร์ชันใหม่ของ Actions ออกมาหรือยัง ถ้ามี บอทจะสร้างการแจ้งเตือนแบบ Pull Request (การขอรวมโค้ดใหม่เข้าโปรเจกต์) ให้เราเข้าไปรีวิวโค้ดก่อนกดตกลงอัปเดต วิธีนี้ทำให้เรามั่นใจว่าทุกการเปลี่ยนแปลงผ่านสายตาเราก่อนเสมอ
วิธีตั้งค่าให้สร้างไฟล์ .github/dependabot.yml ไว้ในโปรเจกต์ของคุณ:
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly" # ให้บอทตรวจทุกสัปดาห์
คำอธิบายโค้ด:
package-ecosystem: "github-actions"บอกให้บอทโฟกัสแค่เรื่อง Actions เท่านั้นinterval: "weekly"คือการตั้งให้บอททำงานทุกสัปดาห์ เพื่อไม่ให้เราโดนแจ้งเตือนถี่เกินไปจนรำคาญ- เมื่อบอทพบเวอร์ชันใหม่ มันจะสร้าง Pull Request ให้เราเข้าไปตรวจสอบรหัส SHA ใหม่ก่อนกด Merge (รวมโค้ด)
ผลลัพธ์ที่เกิดขึ้น: คุณจะได้รับการแจ้งเตือนอัตโนมัติเมื่อมีเวอร์ชันใหม่ ทำให้การอัปเดตโค้ดเป็นเรื่องปลอดภัยและเป็นระเบียบ ไม่ต้องกังวลว่าจะพลาดโค้ดอันตรายที่แอบแฝงมา
จำกัดขอบเขตการใช้งานด้วย Allowlist
นอกจากการล็อกรหัสแล้ว เรายังสามารถจำกัดได้ว่า Workflow ของเราอนุญาตให้ดึงโค้ดมาจากใครได้บ้าง ซึ่งเรียกว่าการทำ Allowlist (รายการที่อนุญาตให้ผ่านได้) เพื่อไม่ให้ใครก็ตามในทีมเผลอไปดึงโค้ดจากแหล่งที่ไม่น่าเชื่อถือมาใช้งานในระบบของเรา
การทำแบบนี้เปรียบเหมือนการตั้งระบบรักษาความปลอดภัยที่หน้าประตูโรงงาน ที่จะให้เฉพาะพนักงานที่ลงทะเบียนไว้เท่านั้นที่เดินเข้ามาได้ การทำเช่นนี้จะช่วยลดความเสี่ยงจากการที่คนในทีมอาจจะเผลอไปใช้ Action ที่มีชื่อคล้ายกับของจริงแต่เป็นของปลอม (Typosquatting)
คุณสามารถตั้งค่าได้ที่เมนู Settings ของโปรเจกต์หรือระดับองค์กร:
- ไปที่ Settings > Actions > General
- เลือก Allow specified actions
- ระบุชื่อผู้สร้างที่เชื่อถือได้ เช่น
actions/*หรือmy-org/*
ข้อควรระวัง: การทำ Allowlist ไม่ได้แปลว่าคุณไม่ต้องล็อกเลข SHA นะครับ มันเป็นเพียงด่านแรกที่ช่วยกรองของปลอมออกไป แต่ในด่านที่สอง คุณก็ยังต้องใช้รหัส SHA ล็อกโค้ดตัวที่อนุญาตแล้วอยู่ดี เพื่อความปลอดภัยสองชั้น
สรุป: สร้างระบบที่ปลอดภัยเริ่มจากจุดเล็กๆ
การป้องกัน Poisoned Dependencies ไม่ใช่เรื่องไกลตัวหรือเรื่องของคนเก่งเท่านั้น แต่มันคือ พื้นฐานความรับผิดชอบ ของโปรแกรมเมอร์ทุกคนที่ต้องใส่ใจตั้งแต่เริ่มเขียนโค้ด การใช้เลขเวอร์ชันแบบลอยตัวอาจจะดูสะดวก แต่ความเสี่ยงที่ตามมานั้นไม่คุ้มค่าเลยกับการที่โค้ดหรือข้อมูลของลูกค้าต้องหลุดออกไป
สรุปขั้นตอนง่ายๆ ที่คุณนำไปใช้ได้วันนี้เลย:
- เปลี่ยนจากการใช้
@v4มาเป็นการใช้รหัส Commit SHA 40 หลักแทน - ใส่คอมเมนต์ระบุเวอร์ชันที่แท้จริงไว้ท้ายบรรทัดเพื่อให้อ่านง่าย
- เปิดใช้งาน Dependabot เพื่อให้มันช่วยแจ้งเตือนตอนมีอัปเดต
- คอยตรวจสอบ Pull Request ทุกครั้งก่อนกด Merge เข้าสู่โปรเจกต์
ตัวอย่างการนำไปใช้จริง: สมมติว่าคุณกำลังทำโปรเจกต์เว็บแอปพลิเคชันแรกในชีวิต แทนที่จะเขียน uses: actions/checkout@v4 ให้คุณไปดูที่หน้าเว็บของ actions/checkout ใน GitHub แล้วก๊อปปี้รหัส SHA ของเวอร์ชันล่าสุดมาแปะแทน เท่านี้คุณก็เป็นโปรแกรมเมอร์ที่ใส่ใจเรื่องความปลอดภัยมากกว่าคนทั่วไปแล้วครับ เริ่มต้นจากจุดเล็กๆ แบบนี้จะทำให้คุณเป็นโปรแกรมเมอร์ที่เก่งและน่าเชื่อถือในสายตาของเพื่อนร่วมทีมแน่นอน
ที่มา: How to Prevent Poisoned GitHub Actions Dependencies — freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More