พบแพ็กเกจ npm ที่เป็นอันตรายในบริการต่างๆ ของ Red Hat Cloud Services
(github.com/RedHatInsights)- ประเด็นนี้ยังอยู่ในสถานะ เปิดอยู่ และ ณ เวลาที่เขียน ยังไม่มีผู้รับผิดชอบ ไมล์สโตน หรือบรานช์/PR ที่เชื่อมโยงไว้
- มีการลงทะเบียนเป็นประเด็นด้านความปลอดภัยว่าพบเวอร์ชันที่เป็นอันตรายใน npm release หลายรายการภายใต้สโคป
@redhat-cloud-services/ - เอกสารอ้างอิงที่แนบมามีบทวิเคราะห์ของ StepSecurity บทวิเคราะห์ และ ผลการค้นหา OSS Security Feed
- รายการผลกระทบที่อัปเดตแล้วรวม 32 แพ็กเกจ เช่น
@redhat-cloud-services/chrome,frontend-components,rbac-client,types,vulnerabilities-client - เวอร์ชันที่ถูกบุกรุกในตารางส่วนใหญ่มีแพ็กเกจละ 3 เวอร์ชัน โดย
@redhat-cloud-services/vulnerabilities-clientมีเพียงสองเวอร์ชันคือ2.1.9,2.1.11 - เมื่อนับจากตารางทั้งหมด สามารถรวมเวอร์ชันที่ถูกบุกรุกได้ 95 เวอร์ชัน และชื่อ PR ภายนอกที่กล่าวถึงแยกต่างหากก็ชี้ไปที่
95 versionsเช่นกัน - มีทั้งตระกูล
@redhat-cloud-services/frontend-components-*และแพ็กเกจ*-clientหลายรายการรวมอยู่ด้วย จึงไม่ใช่ปัญหาของแพ็กเกจเดียว แต่เป็นปัญหาการรีลีสทั่วทั้งสโคปเดียวกัน - ในคอมเมนต์ มีคำถามว่า “What are these?” และมีคำตอบว่า “all that module is pwned” สะท้อนความเข้าใจร่วมกันว่ารายการทั้งหมดถูกเจาะแล้ว
- DanielRuf ระบุว่าได้เพิ่มเหตุการณ์นี้เข้าไปใน supply-chain-incidents แล้ว
- ในกิจกรรมบน GitHub มีทั้งสรุปเนื้อหาที่อ้างถึงอิชชูนี้และ PR ที่เกี่ยวกับการตรวจจับ แต่ในเนื้อหาหลักยังไม่มีการระบุการวินิจฉัย มาตรการบรรเทา การลบออก หรือเวอร์ชันที่แก้ไขแล้วจากฝั่ง Red Hat
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
หวังว่าจะพอขอยืมเธรดนี้กลับมาคุยเรื่องการตั้งค่า cooldown ได้อีกครั้ง เพราะกรณีของ axios, tanstack, @redhat-cloud-services และการโจมตี supply chain บน npm หลายครั้งล่าสุด น่าจะป้องกันได้ถ้ามี cooldown
ถ้าใช้ Artifactory/Nexus ก็มักจะมีอยู่แล้ว และถึงไม่มี การตั้งค่าก็ทำได้ง่าย การเจาะ npm หรือ PyPI ส่วนใหญ่ถูกถอดลงภายในไม่กี่ชั่วโมง ดังนั้นใช้วิธีเมินแพ็กเกจที่เพิ่งปล่อยมาไม่ถึง N วันก็เพียงพอแล้ว 1 วันก็ได้ผล, 3 วันก็กำลังดี, 7 วันอาจจะมากไปหน่อยแต่ก็ใช้ได้
pnpm รุ่นล่าสุดใส่ cooldown 1 วันมาเป็นค่าเริ่มต้นแล้ว: https://pnpm.io/supply-chain-security
ถ้าอยากให้จบในคลิกเดียว ใช้ https://depsguard.com ได้ เป็น CLI ที่เพิ่ม cooldown และค่าตั้งแนะนำให้กับ npm, pnpm, yarn, bun, uv, dependabot และผมเป็นผู้ดูแลมันเอง
ยังมี https://cooldowns.dev ที่โฟกัสเรื่อง cooldown มากกว่า รวมถึงสคริปต์ช่วยตั้งค่าในเครื่องด้วย ทั้งหมดเป็นโอเพนซอร์ส/ฟรี
ถ้าคุณแก้
~/.npmrcเองได้ก็อาจไม่จำเป็น แต่ถ้ารอบตัวมีคนที่ต้องการแค่การแก้แบบคลิกเดียว มันอาจช่วยให้หลบการโจมตีครั้งถัดไปได้แต่ถ้าต้องแพตช์ CVE ร้ายแรงตัวใหม่ ก็ต้องข้าม cooldown ให้ได้ ซึ่งแต่ละเครื่องมือก็มีวิธีข้ามของตัวเอง ช่วงไม่กี่เดือนที่ผ่านมาแม้ผมจะไม่มีตัวเลขเป๊ะ ๆ แต่แม้อยู่ในยุคการค้นพบช่องโหว่แบบ Mythos ก็ดูเหมือนว่าความเสี่ยงจากการโจมตีซอฟต์แวร์ซัพพลายเชนอย่าง การปล่อยเวอร์ชันอันตราย จะสูงกว่าความเสี่ยงจาก CVE zero-day ใหม่เสียอีก
~/.npmrcแล้วเพิ่มหนึ่งบรรทัดได้ แต่ก็ยังรู้สึกว่าคนที่ต้องการการแก้แบบคลิกเดียวนั้นเป็นกลุ่มที่เล็กมากการบอกว่า “security fix ควรมีเฉพาะ security fix และไม่ควรพ่วงฟีเจอร์อื่นมาด้วย” เป็นสิ่งที่พูดได้อย่างสมเหตุสมผล แบบนั้นทั้งนักวิจัยด้านความปลอดภัยและเครื่องมือต่าง ๆ ก็จะตรวจสอบได้ง่ายขึ้น
release ทั่วไปก็ควรใช้ cooldown ส่วน security fix ก็ควรไม่มี cooldown หรืออย่างน้อยก็สั้นกว่ามาก
ระบบแบบ Debian ที่มีเซิร์ฟเวอร์เสถียรมาก และตั้ง unattended upgrades ให้ใช้กับ security fix เท่านั้นได้ เป็นแนวทางที่น่าเอาอย่าง แพ็กเกจรีลีสแบบใหม่นี้ก็นักวิจัยด้านความปลอดภัยตรวจสอบได้ง่ายกว่าด้วย
ในทุกเธรดแบบนี้ มักมีคอมเมนต์ประชดว่าการโจมตีประเภทนี้มีแค่ใน npm หรือทำเหมือนไม่มีมาตรการอะไรเลย แต่ผมว่าแบบนั้นไม่ยุติธรรม
ก็มีคอมเมนต์จำนวนมากที่พูดถึงฟีเจอร์ดี ๆ อย่าง delay line และ pnpm ที่ถูกนำมาใช้เพื่อปกป้องผู้ใช้แพ็กเกจ
ส่วนที่พูดถึงกันน้อยกว่าคือ เครื่องมือฝั่งผู้ดูแลแพ็กเกจ นี่แหละ MFA สำหรับการเผยแพร่: https://docs.npmjs.com/requiring-2fa-for-package-publishing-..., และ trusted publishers ที่มีให้ใช้มาตั้งแต่ราว 1 ปีก่อน: https://docs.npmjs.com/trusted-publishers
ไม่นานมานี้ก็มี staged publishing ออกมาด้วย ซึ่งรวมข้อดีของทั้งสองอย่างเข้าด้วยกัน: https://docs.npmjs.com/staged-publishing
ตอนนี้คุณสามารถเผยแพร่จาก CI ได้โดยไม่ต้องใช้ static credentials และกำหนดให้ผู้ดูแลอนุมัติด้วย MFA ก่อนที่จะถูกเผยแพร่จริงไปยัง registry ได้ หากต้องการ ก็ยังสามารถใช้การป้องกันของ GitHub Actions Environments เพื่อบังคับให้มีการอนุมัติหลายชั้นหรือหน่วงเวลาในฝั่ง CI ได้ด้วย
ชุมชนควรสนับสนุนให้มีการนำกลไกป้องกันการเผยแพร่แบบนี้ไปใช้ ไม่อย่างนั้นปัญหานี้ก็จะเกิดขึ้นต่อไป
ถ้าอย่างนั้นแพ็กเกจอันตรายก็คงได้ดาวสีเขียวด้วย และทำให้ผู้ใช้สบายใจว่า “ถูก build และ signed พร้อมการพิสูจน์แหล่งที่มา”
[1] https://lwn.net/Articles/1075742/
เป็นเรื่องดีที่มีงานเพื่อป้องกันสิ่งนี้อยู่ แต่ถึงอย่างนั้นมันก็ยังเกิดขึ้นต่อเนื่อง ขำในความหมายแบบ “เอาอีกแล้วสินะ”
จากมุมคนนอก การพัฒนาเว็บมีพลังแบบแดนเถื่อนยุคบุกเบิกอยู่พอสมควร มีทั้งความเปลี่ยนแปลงได้ตลอด, dynamic typing, มาตรฐานและเฟรมเวิร์กที่เปลี่ยนไม่หยุด, continuous deployment, CDN, แคมเปญ A/B แบบเรียลไทม์, dependency จำนวนมาก, และข้อมูลผู้ใช้ที่อ่อนไหวซึ่งกระจายอยู่บนหลายโครงสร้างพื้นฐาน
ผมไม่ได้บอกว่ามุมมองนี้ถูกต้อง หรือคิดว่าท่าทีแบบ “เห็นไหมล่ะ” จะเหมาะสม แต่ก็เข้าใจได้ว่าทำไมคนถึงคิดแบบนั้น
โดยเฉพาะคือไม่มีข้อไหนเลยที่จะหยุดไม่ให้ backdoor ของ xz-utils หลุดเข้าไปในแพ็กเกจที่เผยแพร่ได้ xz-utils ยังคงเป็นหมุดหมายของการเจาะ upstream อย่างแนบเนียน
บั๊กที่แท้จริงตรงนี้ไม่ใช่ว่าเราต้องยืนยันตัว upstream ที่ไว้ใจอยู่แล้วให้ดีขึ้น แต่คือเราไม่สามารถเชื่อถือ upstream ให้เป็นแหล่งที่มาด้านความปลอดภัยเพียงหนึ่งเดียวได้ upstream คือกลุ่มแฮ็กเกอร์ที่มักไม่ได้สนใจและก็ไม่ได้เก่งเรื่อง release engineering ที่แข็งแรงนัก
แต่ก็มีคนที่ทำสิ่งนั้นได้ดีอยู่ ทางออกของโลก Linux และสิ่งที่ช่วยเราจาก xz-utils คือการมี ชั้นมนุษย์ชั้นที่สอง ที่คอยตรวจทาน ตรวจสอบ แพ็กเกจ และปรับแต่ง upstream ที่แฮ็กเกอร์สร้างมาเพื่อผู้ใช้
คนกลุ่มนี้มีสายตาอีกแบบหนึ่ง มีความต้องการของผู้บริโภคอีกแบบ และมีมาตรฐานคุณภาพอีกแบบ จึงจับทั้งบั๊กและความประสงค์ร้ายที่ upstream ยังไม่พร้อมจะจับได้
NPM, cargo, PyPI และอื่น ๆ ดูเหมือนจะยังคิดว่าตัวเองหลีกเลี่ยงความต้องการแรงงานมนุษย์แบบนี้ได้ แต่ทำไม่ได้ โดยเฉพาะ ecosystem ของ NPM มีนักพัฒนาเว็บจำนวนมากที่คุ้นกับการออกรุ่นเร็วมาก ข้อกำหนดด้านความเข้ากันได้ที่หลวม และการนำกลับมาใช้ซ้ำอย่างสุดขั้ว จึงยิ่งเป็นเหตุผลว่าทำไมเรื่องแบบนี้จึงเห็นในแพ็กเกจ node บ่อยกว่า Python หรือ Rust
บริษัทของเราใช้ yarn 4 และมีตัวเลือกที่บล็อกการติดตั้ง npm package ในช่วงสองสามวันแรกหลังจากปล่อยแพ็กเกจ ดูเหมือนว่าการโจมตีแบบนี้ส่วนใหญ่จะถูกจับได้ภายในช่วงนั้น (1~3 วัน)
https://gist.github.com/mcollina/b294a6c39ee700d24073c0e5a4e...
แพ็กเกจ axios ถูกเจาะ และข้อมูลรับรองของผู้เขียนก็ถูกขโมยไปด้วย ทำให้ทุกครั้งที่พยายามแก้ไขก็ถูกทำให้ใช้การไม่ได้อีก: https://www.trendmicro.com/en_us/research/26/c/axios-npm-pac...
ยูทิลิตี xz มี backdoor แฝงอยู่เป็นเวลา 2 เดือน: https://gigazine.net/gsc_news/en/20240403-timeline-of-xz-ope...
นักวิจัยนักศึกษาคนหนึ่งรับโอนสิทธิ์ดูแลแพ็กเกจ Python ctx และ PHPass แล้วปล่อยการแก้ไขที่เป็นอันตราย โดยใช้เวลามากกว่า 7 วันกว่าจะตรวจพบและแก้ไขได้: https://infosecwriteups.com/how-i-hacked-ctx-and-phpass-modu...
Kaspersky พบหลายแพ็กเกจบน PyPI ที่ถูกใช้โจมตีนานกว่าหนึ่งปี: https://www.kaspersky.com/about/press-releases/kaspersky-unc...
แพ็กเกจ “LoftyLife” ถูกใช้โจมตีอยู่นานหลายเดือน: https://securelist.com/lofylife-malicious-npm-packages/10701...
ถ้าหน้าต่างการโจมตีเปลี่ยนเป็น 7 วัน การโจมตีใหม่แบบนี้ทั้งหมดก็คงจะใส่ ระเบิดเวลา ที่ไม่ทำงานจนกว่าจะถึงวันที่ 8
pnpmก็มีฟีเจอร์เดียวกัน และเปิดใช้เป็นค่าเริ่มต้นตั้งแต่v11: https://pnpm.io/settings#minimumreleaseageมีข้อเสนออยู่หลายอย่าง dependency cooldown 1~2 วันดูเหมือนจะมีประสิทธิภาพมาก โดยไม่กระทบความสามารถในการแพตช์ CVE
ทุกที่ที่มีการรันโค้ด เช่น
npm install,npm testควรทำงานในสภาพแวดล้อมที่ไม่มีสิทธิ์พิเศษ บน GitHub Actions การแยกงานที่ build·test อาร์ติแฟกต์ ออกจากงานที่ deploy·sign เป็นต้น ทำได้ค่อนข้างง่าย ถ้าใช้ AI ก็แค่เพิ่ม skill/guide ที่บังคับแพตเทิร์นนี้ถ้าใช้ GitHub Actions การติดตั้ง zizmor เวอร์ชันล่าสุดจะช่วยยกระดับสถานะความปลอดภัยได้มาก
มาตรการข้อที่สองจะทำให้มันไม่สามารถ “แพร่กระจายแบบหนอน” ได้อีก และช่วยลดปัญหาส่วนใหญ่ที่มีอยู่ตอนนี้ ส่วนข้อแรกช่วยซื้อเวลาให้บริษัทต่าง ๆ รับมือการโจมตีได้ ผู้ขายบางรายในด้านนี้ก็น่าลองประเมินดู
เพิ่งพูดเองแท้ ๆ ว่าควรหน่วงแพ็กเกจใหม่ เลยฟังดูตลกเหมือนมุก
รัน Maven proxy ไว้ในเครื่อง และให้ทุก build ทำในคอนเทนเนอร์ ส่วน Python, npm, Go ใช้แค่ public repository ดังนั้น build พวกนี้ก็ทำในคอนเทนเนอร์ได้เหมือนกันโดยไม่ต้องมี repository proxy
เป็นวันเดียวกับที่ Red Hat และ IBM ประกาศ Project Lightwell เพื่อช่วยตรวจจับและแก้ไขช่องโหว่ในซัพพลายเชนพอดี
https://www.redhat.com/en/lightwell
เมื่อไม่กี่วันก่อนเห็น rant ที่น่าสนใจนี้: https://github.com/uNetworking/uWebSockets.js/blob/master/mi...
พูดก็มีเหตุผลอยู่ที่ว่าวิธีที่ถูกต้องคือฟอร์ก dependency ทุกตัวที่ใช้ แล้วคอยตรวจทานและ merge จาก upstream เมื่อจำเป็น พร้อมติดตั้งจากรีโปของตัวเอง แต่อาจจะยุ่งยากมาก
มันดีต่อการลดหรือเลิกพึ่งผู้ให้บริการโฮสต์ dependency ภายนอก ดึง dependency เข้ามาอยู่ในเครื่องมือ code review ของตัวเอง และรับประกัน reproducible build ได้ในระยะยาว
Packj(https://github.com/ossillate-inc/packj) ตรวจจับ dependency อันตรายใน PyPI/NPM/Ruby/PHP ฯลฯ ด้วยการวิเคราะห์พฤติกรรม โดยสแกนตัวบ่งชี้การบุกรุก เช่น การรันเชลล์ การใช้ SSH key การสื่อสารผ่านเครือข่าย และการใช้ decode+eval ด้วย static+dynamic code analysis นอกจากนี้ยังตรวจสอบคุณสมบัติ metadata หลายอย่างเพื่อหา actor ไม่ประสงค์ดี เช่น typosquatting
ราวหนึ่งสัปดาห์ก่อนฉันลบ Node ออกจากโน้ตบุ๊ก แล้วรู้สึกดีมาก
เพื่อจำกัดขอบเขตความเสียหายแม้จะโดน exploit แบบโชคร้าย ตอนนี้เลยพยายามทำทุกอย่างใน development container หรือ sandbox อื่น ๆ ผู้โจมตีอาจขโมยโทเค็น Claude ไปได้ แต่คงหนีออกจากคอนเทนเนอร์มารื้อโฮมไดเรกทอรีได้ไม่ง่าย
cooldown และ allowlist ของสคริปต์ติดตั้งเป็นชั้นป้องกันเสริมที่ดี โดยเฉพาะใน CI แต่สิ่งที่ควรเปลี่ยนจริง ๆ คือโมเดลสิทธิ์ของระบบปฏิบัติการ ค่าเริ่มต้นที่เชื่อถือซอฟต์แวร์ third-party ด้วยสิทธิ์ของผู้ใช้ทั้งหมดนั้นใช้การไม่ได้อีกต่อไปแล้ว
น่าจะควรใส่ลิงก์ประกาศต้นฉบับไว้
https://www.stepsecurity.io/blog/multiple-redhat-cloud-servi...
ตอนติดตั้งแพ็กเกจ เดี๋ยวนี้ฉันเริ่มติดนิสัยใช้แฟล็ก
--before=2026-05-30แล้ว มันจะบังคับให้เลือกเวอร์ชันที่ปล่อยก่อนวันที่กำหนด โดยปกติก็เลือกประมาณ 5 วันก่อนหน้าhttps://docs.npmjs.com/cli/v11/using-npm/config#min-release-...
pnpmและตั้งminimumReleaseAgeไว้ค่อนข้างกว้างhttps://pnpm.io/settings#minimumreleaseage
โชคดีที่ตั้งแต่ v11 ก็เปิดไว้เป็นค่าเริ่มต้นแล้ว
min-release-age=5ใน.npmrcที่ root ของโปรเจ็กต์NPM พังมาตั้งแต่การออกแบบ และ NIH syndrome ที่แพร่หลายอยู่ในชุมชนก็ทำให้แม้แต่มาตรการง่าย ๆ ก็ทำไม่ได้
เพราะรับแพ็กเกจภายนอกมาใช้แทนการพัฒนาเองเยอะเกินไป โปรเจ็กต์ npm จึงมักมี dependency tree ที่ใหญ่และซับซ้อน มีคนบ่นมานานแล้วว่าแพ็กเกจอย่าง https://www.npmjs.com/package/is-windows สร้างช่องโหว่ที่อาจเกิดขึ้นและภาระการดูแลรักษา ทั้งที่เขียนโค้ดแบบเดียวกันเองได้ง่ายมาก
แต่แน่นอนว่าไม่จำเป็นต้องทำทุกฟังก์ชันขึ้นมาใหม่ แค่ทำส่วนที่ต้องใช้ก็พอ
ยิ่งไปกว่านั้น เวลาเขียนแค่ฟังก์ชันเดียว ก็ไม่ต้องสร้าง abstraction หรือ interface ฟังก์ชันเสริมด้วย เลยถูกกว่า และน่าจะผสานเข้ากันได้ดีกว่า
อีกความเข้าใจผิดหนึ่งคือคิดว่าจะสร้างบั๊กและช่องโหว่ขึ้นมาเอง ซึ่งถ้าเป็นโปรแกรมเมอร์ที่ไม่เก่งก็อาจใช่ แต่ก็จะเลี่ยงหมวดหมู่ของช่องโหว่ที่เกิดขึ้นตรงรอยต่อการผสานไลบรารีสองตัวที่ไม่ได้ออกแบบมาให้เข้ากันอย่างพอดีได้ และกรณีแบบนั้นมีอยู่บ่อย