3 คะแนน โดย GN⁺ 2025-10-31 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • พบว่ามี แพ็กเกจอันตรายสำหรับขโมยข้อมูลรับรองมากกว่า 100 รายการ ถูกอัปโหลดขึ้นสู่รีโพซิทอรี NPM โดยไม่ถูกตรวจพบมาตั้งแต่เดือนสิงหาคม และมียอดดาวน์โหลดรวมมากกว่า 86,000 ครั้ง
  • บริษัทความปลอดภัย Koi รายงานว่าแคมเปญโจมตีที่ตั้งชื่อว่า PhantomRaven ได้ใช้ประโยชน์จากฟีเจอร์ Remote Dynamic Dependencies (RDD) ของ NPM เพื่อเผยแพร่แพ็กเกจอันตราย 126 รายการ
  • RDD เป็น โครงสร้างที่ทำให้แพ็กเกจสามารถดาวน์โหลดโค้ด dependency แบบไดนามิกจากโดเมนที่ไม่น่าเชื่อถือได้ จึงไม่ถูกตรวจจับโดยเครื่องมือวิเคราะห์แบบสแตติก
  • ผู้โจมตีใช้ฟีเจอร์นี้เพื่อ ดาวน์โหลดโค้ดอันตรายผ่านการเชื่อมต่อ HTTP และใน metadata ของแพ็กเกจจะแสดงเป็น “0 Dependencies” ทำให้นักพัฒนาและสแกนเนอร์ด้านความปลอดภัยไม่ทันสังเกต
  • ช่องโหว่เชิงโครงสร้างนี้สะท้อนให้เห็นถึง ข้อจำกัดของการดูแลความปลอดภัยในระบบนิเวศ NPM และความเสี่ยงของกลไกการติดตั้งอัตโนมัติ

การแพร่กระจายของแพ็กเกจอันตรายในรีโพซิทอรี NPM

  • ผู้โจมตีอาศัย จุดอ่อนเชิงโครงสร้าง ของรีโพซิทอรีโค้ด NPM เพื่ออัปโหลด แพ็กเกจสำหรับขโมยข้อมูลรับรอง มากกว่า 100 รายการตั้งแต่เดือนสิงหาคม
    • แพ็กเกจส่วนใหญ่ถูกเผยแพร่โดยไม่ถูกตรวจพบ และมียอดดาวน์โหลดสะสม มากกว่า 86,000 ครั้ง
  • บริษัทความปลอดภัย Koi ตั้งชื่อการโจมตีนี้ว่า แคมเปญ PhantomRaven และวิเคราะห์ว่ามีการใช้ประโยชน์จากฟีเจอร์เฉพาะของ NPM
    • ตามข้อมูลของ Koi จากแพ็กเกจอันตราย 126 รายการ มีราว 80 รายการที่ยังคงอยู่บน NPM ณ เวลาที่เขียนบทความ

โครงสร้างที่เปราะบางของ Remote Dynamic Dependencies (RDD)

  • RDD เป็นฟีเจอร์ที่อนุญาตให้แพ็กเกจ ดาวน์โหลดโค้ด dependency แบบไดนามิกจากเว็บไซต์ภายนอก
    • โดยปกติ dependency จะถูกดาวน์โหลดจาก โครงสร้างพื้นฐานที่เชื่อถือได้ ของ NPM แต่ RDD อนุญาตให้ดาวน์โหลดผ่าน การเชื่อมต่อที่ไม่เข้ารหัส เช่น HTTP ได้ด้วย
  • ผู้โจมตี PhantomRaven ใช้ฟีเจอร์นี้เพื่อตั้งค่าให้ดาวน์โหลดโค้ดจาก URL อันตราย เช่น http://packages.storeartifact.com/npm/unused-imports
    • dependency ลักษณะนี้ มองไม่เห็นทั้งสำหรับนักพัฒนาและสแกนเนอร์ด้านความปลอดภัย และในข้อมูลแพ็กเกจจะแสดงเป็น “0 Dependencies”
  • เนื่องจากฟีเจอร์ติดตั้งอัตโนมัติของ NPM ทำให้โค้ด dependency ที่ ‘มองไม่เห็น’ เหล่านี้ถูกเรียกใช้งานโดยอัตโนมัติ

ข้อจำกัดของการตรวจจับโดยเครื่องมือความปลอดภัย

  • Oren Yomtov จาก Koi ระบุว่า “PhantomRaven เป็นกรณีที่ใช้ประโยชน์จาก จุดบอดของเครื่องมือความปลอดภัยที่มีอยู่ได้อย่างแยบยล
    • RDD ไม่ถูกตรวจจับโดยเครื่องมือวิเคราะห์แบบสแตติก
  • ด้วยเหตุนี้ ผู้โจมตีจึงสามารถ หลบเลี่ยงกระบวนการตรวจสอบความปลอดภัย และเผยแพร่โค้ดอันตรายได้

ปัจจัยเสี่ยงเพิ่มเติม

  • Koi อธิบายว่า dependency ที่ดาวน์โหลดผ่าน RDD จะถูก ดาวน์โหลดใหม่จากเซิร์ฟเวอร์ของผู้โจมตีทุกครั้งที่มีการติดตั้ง
    • ไม่มีแคชหรือการจัดการเวอร์ชัน ทำให้แม้จะเป็นแพ็กเกจเดียวกันก็ยังมีความเป็นไปได้ที่จะถูกฝัง โค้ดอันตรายที่แตกต่างกันในแต่ละครั้งที่ติดตั้ง
  • โครงสร้างการดาวน์โหลดแบบไดนามิกเช่นนี้ทำให้ การตรวจสอบความถูกต้องสมบูรณ์ของแพ็กเกจทำได้ยาก

โครงสร้างและพื้นหลังของ NPM

  • NPM เป็น ตัวจัดการแพ็กเกจสำหรับ JavaScript ที่ดูแลโดย npm, Inc. ซึ่งเป็นบริษัทในเครือของ GitHub
    • เป็นตัวจัดการแพ็กเกจพื้นฐานของ Node.js และประกอบด้วยไคลเอนต์บรรทัดคำสั่งกับ npm registry
    • ภายใน registry มีทั้งแพ็กเกจสาธารณะและแพ็กเกจส่วนตัวแบบเสียเงินเก็บอยู่ และสามารถค้นหาได้ผ่านเว็บไซต์
  • เหตุการณ์ครั้งนี้ถูกชี้ว่าเป็น ตัวอย่างที่แสดงให้เห็นว่าโครงสร้างการจัดการ dependency อัตโนมัติของ NPM สามารถถูกนำไปใช้โจมตีได้

ประเด็นอื่นที่กล่าวถึง

  • ช่วงท้ายบทความมีการกล่าวถึง ความเห็นที่ว่าควรบล็อกการรัน JavaScript ที่ไม่จำเป็น
    • อย่างไรก็ตาม เหตุโจมตีครั้งนี้ถูกชี้ว่าเป็น กรณีที่แม้แต่โค้ด JavaScript ที่จำเป็นก็ยังถูกนำไปใช้ในทางที่ผิดได้

2 ความคิดเห็น

 
developerjhp 2025-11-25

ผมได้ลองทำสคริปต์สแกนเนอร์แบบเรียลไทม์ไว้ครับ

ที่ path ของรีโพซิทอรีที่น่าสงสัย
npx sha1-hulud-scanner
ให้พิมพ์คำสั่งนี้ได้เลยครับ

ซอร์สโค้ด : https://github.com/developerjhp/sha1-hulud-scanner

 
GN⁺ 2025-10-31
ความเห็นจาก Hacker News
  • ช่วงนี้ฉันตั้ง alias ให้รันคำสั่ง npm ภายใน Docker container
    แบบนี้จะไม่เผย environment variables ของฉัน, ไม่เข้าถึงไฟล์นอกไดเรกทอรีปัจจุบัน, และไม่สามารถเข้าถึงไฟล์ตั้งค่าอย่าง .bashrc ได้
    อ้างอิง: Run tools inside Docker

    • นั่นดูเป็นการ sandbox ที่หนักเกินไปหน่อย ยังไง npm ก็ไปดาวน์โหลด โค้ดตามอำเภอใจ ที่จะถูกรันทันทีอยู่ดี
      ฉันแนะนำ pnpm แทน เพราะค่าเริ่มต้นจะไม่รัน lifecycle scripts และสามารถกำหนด whitelist ของสคริปต์ที่อนุญาตได้
    • การทำให้ post-install scripts ดูเป็นปีศาจนั้นมีแต่จะสร้าง ภาพลวงตาด้านความปลอดภัย ที่ผิด ๆ
      ถ้าต้องการการป้องกันจริง ๆ ต้องรันทั้งกระบวนการ ไม่ใช่แค่ตอนติดตั้ง ภายใน sandbox
      การบล็อกแค่ post-install แบบตอนนี้เป็นมาตรการครึ่ง ๆ กลาง ๆ เท่านั้น การโจมตีซัพพลายเชนกำลังอันตรายขึ้นเรื่อย ๆ
    • มีช่องทางโจมตีเยอะเกินไป ถ้ามีเจตนาร้าย ก็สามารถทำ typosquatting ชื่อ ปลั๊กอินหรือ LSP ยอดนิยม เพื่อให้โค้ดรันอัตโนมัติเมื่อเปิด editor ได้
      ถ้า neovim หรือ vscode ติดเชื้อ ก็ทำเรื่องอันตรายได้มากพอแล้วภายใต้สิทธิผู้ใช้
    • ฉันใช้ sandbox-run
      alias ธรรมดาใช้ได้กับ node/npm แต่ใช้กับโปรแกรมอื่นยาก เพราะต้อง mount ทรัพยากรที่ container ต้องใช้
    • แต่สุดท้ายก็ยังดาวน์โหลด แพ็กเกจอันตราย ได้อยู่ไม่ใช่หรือ? ตัว dependency เองก็อาจติดเชื้อได้
  • ฉันสงสัยมานานแล้วว่าทำไมคนถึงรัน npm บนระบบกันแบบไม่คิดอะไร
    ในฐานะคนที่คุ้นกับ reproducible builds แบบ make ฉันช็อกมากที่ npm ดาวน์โหลดของต่างกันทุกครั้งและให้ผลลัพธ์ต่างกัน
    แม้แต่การสร้าง CSS ก็ยังผูกกับ dependency ของ npm ซึ่งดูแปลกมาก เลยเคยลอง freeze environment ของ npm ทั้งก้อนไว้ใน Docker แต่ดูเหมือนเป็นสงครามที่แพ้อยู่ดี

    • ทุกวันนี้ package manager แทบทั้งหมดก็ทำงานแบบนั้น maven, nuget, pip, npm ก็เหมือนกัน
      ถ้ายังพึ่ง package manager ของดิสโทรแบบเมื่อก่อน ก็คงไม่มี ecosystem ที่ขยับเร็ว แบบทุกวันนี้
      แต่อย่างน้อยตอนนี้ก็เริ่มมี package manager รุ่นใหม่ที่เสริมความปลอดภัยมากขึ้น การด่าที่เครื่องมือโดยไม่เข้าใจเหตุผลเบื้องหลังไม่ค่อยถูกนัก
    • การพัฒนา frontend มันเหมือน แดนเถื่อนแบบ “trust me bro” มาก เป็นความรู้สึกเหมือนเอา duct tape มาพัน ๆ ต่อ ๆ กันเพราะวิวัฒนาการของ browser บังคับให้เป็นแบบนั้น
    • ถ้าคุณ freeze npm ไว้ใน Docker แล้ว ก็อยากถามว่าได้ ตรวจสอบ environment นั้นใหม่ทุกครั้งหลังอัปเดต dependency หรือเปล่า
      จริง ๆ แล้ว npm กับ pnpm ก็มี lock file สำหรับตรึง dependency เป็นค่าเริ่มต้นอยู่แล้ว
    • ปัญหาคือ “npm install thing” มันง่ายและต้นทุนต่ำเกินไป
      โอเพนซอร์สจำนวนมากเลยเต็มไปด้วย โค้ดเพื่อใส่ในเรซูเม่มากกว่าคุณภาพจริง และสุดท้ายก็ถูกนำไปใช้สร้างพวกตัวติดตามโฆษณาของบริษัทยักษ์ใหญ่หรือแอปกระเป๋าเงิน
  • npm install ไม่ได้แค่ดาวน์โหลดแพ็กเกจ แต่มัน รันโค้ด ด้วย
    preinstall, install, postinstall hooks ใน package.json จะถูกรันจริง
    มีเหตุผลที่ชอบธรรมอะไรบ้างที่ต้องให้ขั้นตอนติดตั้งสามารถรันคำสั่งตามอำเภอใจได้?
    รายงานที่เกี่ยวข้อง: PhantomRaven npm malware
    อีกกรณีหนึ่ง: บล็อก Socket.dev

    • ที่จริงโครงสร้างแบบนี้มีอยู่ใน package manager รุ่นเก่าอย่าง DEB และ RPM มานานแล้ว
      ตัวอย่างเช่นแพ็กเกจ Linux kernel จะรัน post-install script เพื่อ สร้าง initramfs ใหม่และอัปเดต GRUB
      แพ็กเกจ DEB/RPM ส่วนใหญ่ก็มีสคริปต์ลักษณะนี้ นี่จึงเป็นปัญหาในระดับการออกแบบ
    • ปัญหาคือ npm นั้น ใครก็อัปโหลดแพ็กเกจได้
      ดิสโทร Linux มีระบบ maintainer ที่เชื่อถือได้ และบางครั้งก็สร้าง root of trust แบบ PGP ด้วยตัวเอง
      แต่ npm, pip, rubygems, cargo ฯลฯ นั้นจริง ๆ ก็เป็นเพียง “curl | bash” เวอร์ชันที่ดูดีขึ้นเท่านั้น
    • เช่นโปรเจกต์ Mediasoup เป็นไลบรารีสตรีมมิงที่เขียนด้วย C++ และจะคอมไพล์ซอร์สตอนติดตั้ง
      post-install build แบบนี้จำเป็นเพื่อให้ภาระการดูแลรักษาลดลง
    • Swift Package Manager ก็รันไฟล์ Package.swift จริงเช่นกัน
      แต่ได้ยินมาว่ามีการ sandbox ที่เข้มมาก เลยเอาไปใช้โจมตีได้ยาก
      อ้างอิง: เอกสาร SwiftPM, PackageDescription
    • เพิ่มเติมคือ pnpm v10 ปิด lifecycle scripts ทั้งหมดเป็นค่าเริ่มต้น และให้ผู้ใช้เป็นคนอนุญาตเอง
      การพูดคุยที่เกี่ยวข้อง
  • ดูจากการโจมตี npm ช่วงหลัง ๆ แล้ว ก็เริ่มสงสัยว่าการพัฒนาด้วย npm ยัง ปลอดภัย อยู่ไหม
    ทุกครั้งที่เริ่มโปรเจกต์ React ก็มีแพ็กเกจนับร้อยถูกติดตั้ง ทั้งที่ไม่รู้ด้วยซ้ำว่ามันทำอะไร
    ฝั่ง backend ยังระบุแพ็กเกจที่ต้องใช้แบบชัดเจน แต่ฝั่ง frontend เหมือน กล่องแพนโดราของช่องโหว่

    • จริง ๆ ecosystem ของทุกภาษาก็คล้ายกัน แค่ npm ใหญ่ที่สุดและเป็นข่าวบ่อยกว่าเท่านั้น
    • ฉันติดตั้ง jj ของ Rust แล้วมี 470 แพ็กเกจ ส่วน wan2gp ของ Python มี 211 แพ็กเกจ ก็พอ ๆ กันหมด
    • ecosystem ของ JavaScript เปราะบางต่อการโจมตีในเชิงโครงสร้าง
      เหมือนกรณี xz ที่ dependency แต่ละตัวผูกอยู่กับ บุคคลสุ่ม ๆ และเราต้องหวังว่าพวกเขาจะไม่โดน social engineering
    • dependency ยิ่งน้อยยิ่งดี ศูนย์คือตัวเลขที่สมบูรณ์แบบ นั่นแหละคือชัยชนะที่แท้จริง
    • อนึ่ง PyPI ก็ไม่ได้ปลอดภัย มีกรณีที่ แพ็กเกจปกติถูกฝังโค้ดอันตราย ผ่านการแฮ็ก GitHub Actions ด้วย
  • ทุกครั้งที่พัฒนาด้วยเฟรมเวิร์กอย่าง Angular หรือ Vue ก็รู้สึกกังวล
    พอเห็น dependency หลายพันตัวใน node_modules ก็รู้สึกเหมือน ลางบอกเหตุแห่งหายนะ
    แค่นักพัฒนาโอเพนซอร์สคนหนึ่งโดนฟิชชิงก็อาจติดเชื้อได้ทันที
    ecosystem ของ JavaScript พังตั้งแต่รากฐาน การพิมพ์ผิดแค่ครั้งเดียวก็อาจเปิดทางให้โดนโจมตีซัพพลายเชน
    NuGet หรือ Maven ก็เป็นได้เหมือนกัน แต่ฝั่งนั้นมี standard library ใหญ่กว่า จึงมี dependency น้อยกว่าและรู้สึกควบคุมได้มากกว่า

    • Go ใช้ repo URL แทนชื่อแพ็กเกจ จึงลดปัญหา typosquatting ได้
      ไม่สมบูรณ์แบบ แต่ก็ยังดีกว่าอีกขั้น
    • Deno แก้ปัญหาพวกนี้ได้ นี่คือ ปัญหาเชิงโครงสร้างของ Node.js / npm
  • ในยอดดาวน์โหลด 86,000 ครั้งนั้น ส่วนใหญ่อาจไม่ใช่ผู้ใช้จริง แต่เป็น สแกนเนอร์อัตโนมัติหรือบอต
    พอปล่อยเวอร์ชันใหม่ก็มักถูกดาวน์โหลดหลายร้อยครั้งภายในวันสองวัน แต่ผู้ใช้จริงอาจไม่มีเลยก็ได้
    หมายความว่าผู้ใช้ที่ติดเชื้อจริงอาจมีน้อยมาก

    • ตอนฉันปล่อยไลบรารีเอง ช่วงแรกก็มียอดดาวน์โหลดราว 300 ครั้งต่อสัปดาห์ แล้วหลังจากนั้นประมาณ 100 ครั้ง
      ยังมีการโจมตีที่เล็งชื่อแพ็กเกจซึ่ง AI chatbot สร้างขึ้นแบบหลอนข้อมูล ด้วย มันจึงมากกว่าแค่สถิติธรรมดา
    • หรืออาจเป็น zombie CI ของใครสักคนที่คอยดาวน์โหลดซ้ำ ๆ อยู่ก็ได้
    • แต่ถ้าเป็นการโจมตีที่เล็งชื่อแพ็กเกจปลอมซึ่ง LLM สร้างขึ้น ก็อาจมีนักพัฒนาจำนวนมากติดเชื้อจริงแล้วก็ได้
  • ถ้าอยากดูคำอธิบายการโจมตีแบบละเอียดขึ้น ดูได้ที่ บทความของ BleepingComputer

  • สงสัยว่ามีวิธีตรวจจับหรือกรองแพ็กเกจที่ใช้ HTTP URL เป็น dependency ระหว่าง npm install ไหม
    เพราะมันสามารถส่ง payload ต่างกันตามผู้ร้องขอได้ ทำให้สแกนเนอร์ทั่วไปตรวจจับได้ยาก

  • ในฐานะนักพัฒนาแบบงานอดิเรก ฉันกำลังคิดว่าควรเริ่มป้องกัน การโจมตีซัพพลายเชน แบบนี้อย่างไรดี
    พอตาม tutorial ดัง ๆ แล้วติดตั้ง dependency ไปเรื่อย ๆ ก็เผลอชินชากับเรื่องความปลอดภัย
    ฉันก็รันหลายบริการใน homelab เหมือนกัน เลยกังวลว่าบอตอาจเจาะเข้ามาได้ ควรเริ่มจากตรงไหน?

    • ถ้าแยกรันบริการใน container หรือ VM ก็ช่วย จำกัดความเสียหาย ได้
      ไม่ได้การันตีสมบูรณ์แบบ แต่ก็ดีกว่าปล่อยให้ทั้งเซิร์ฟเวอร์ถูกเจาะมาก
    • มองทุก dependency เป็น ความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้นได้ และใช้เท่าที่จำเป็นจริง ๆ
      การคัดลอกเฉพาะโค้ดที่ต้องใช้มาใช้เองก็เป็นทั้งวิธีเรียนรู้ที่ดีและปลอดภัยกว่า
    • การใช้ รีลีสที่นิยมและมีอายุเกิน 1 ปี จะปลอดภัยกว่า ถ้ามีปัญหาก็มักมีโอกาสถูกค้นพบไปแล้ว
    • มี OS อย่าง FreeBSD ที่ใช้ system package manager
      โครงสร้างแบบนี้ช่วยให้ ยืนยันความน่าเชื่อถือในระดับดิสโทร ได้ โดยไม่ต้องให้ผู้ใช้หลายล้านคนมาตรวจสอบเอง
    • ฉันชอบแพ็กเกจที่มียอดดาวน์โหลดต่อสัปดาห์เกิน 1 ล้านครั้ง และ ไม่มี dependency
      เช่น Hono, Zod
      ช่วงหลังฉันย้ายไปใช้ Bun ซึ่งมีของพื้นฐานอย่าง DB driver หรือ S3 client มาให้ในตัว จึงต้องดาวน์โหลดเพิ่มน้อยลง
  • โครงสร้างที่ดึง dependency มาจาก lifecycle hooks สามารถกลายเป็น จุดเปลี่ยนของการโจมตี ได้ทุกเมื่อ
    วันนี้อาจยังปกติ แต่ภายหลังถ้าเจ้าของบัญชีถูกแฮ็กหรือเปลี่ยนใจ มันก็อาจกลายเป็นโค้ดอันตรายได้
    install hooks ในลักษณะนี้จึงเป็น การออกแบบที่ยั่งยืนไม่ได้ ในท้ายที่สุด