- การโจมตีซัพพลายเชนคือรูปแบบที่มี การอัปเดตที่เป็นอันตราย แอบแฝงเข้ามาในโค้ดโอเพนซอร์ส และ Obsidian ใช้ กลยุทธ์ลดการพึ่งพาภายนอกตั้งแต่ต้น เพื่อลดความเสี่ยงนี้
- ฟีเจอร์ส่วนใหญ่ของแอปถูกพัฒนาขึ้นเองโดยตรง หรือหากจำเป็นก็จะนำ โค้ดที่ fork มา รวมไว้และดูแลภายในโค้ดเบสของตนเอง
- ไลบรารีขนาดใหญ่ที่จำเป็น (pdf.js, Mermaid, MathJax เป็นต้น) จะถูก ตรึงเวอร์ชัน เพื่อความปลอดภัย และจะอัปเดตอย่างระมัดระวังเฉพาะเมื่อมีแพตช์ด้านความปลอดภัยเท่านั้น
- dependency ทั้งหมดจะถูก ตรึงไว้ด้วย lockfile และไม่มีการรันสคริปต์ postinstall เพื่อป้องกันการรันโค้ดตามอำเภอใจระหว่างติดตั้ง
- ด้วย กระบวนการอัปเดตที่รอบคอบและกลยุทธ์หน่วงเวลา นี้ Obsidian จึงสามารถรับมือกับภัยคุกคามที่อาจเกิดขึ้นได้ก่อนที่ชุมชนจะตรวจพบ
การโจมตีซัพพลายเชนคืออะไร
- การโจมตีซัพพลายเชน คือวิธีที่การอัปเดตอันตรายในระบบนิเวศโอเพนซอร์สแฝงตัวเข้ามาในโค้ดที่ถูกแจกจ่าย
- เนื่องจากมีแอปจำนวนมากที่ใช้โค้ดโอเพนซอร์ส การอัปเดตที่เป็นอันตรายเพียงครั้งเดียวจึงสามารถส่งผลกระทบเป็นวงกว้างต่อหลายแอปได้
- Obsidian ออกแบบแอปให้ปลอดภัยขึ้นและลดพื้นผิวการโจมตีเหล่านี้ด้วยกลยุทธ์ ลด dependency ให้น้อยที่สุด
กลยุทธ์ลด dependency : Less is Safer
- Obsidian พึ่งพา ไลบรารีภายนอกจำนวนน้อยมาก เมื่อเทียบกับแอปอื่นในกลุ่มเดียวกัน
- ฟีเจอร์หลัก (เช่น Bases, Canvas) ถูก พัฒนาขึ้นเองแทนการนำไลบรารีภายนอกมาใช้
- วิธีนี้ช่วยให้สามารถ ควบคุมโค้ดที่รันอยู่ได้อย่างสมบูรณ์
- ฟังก์ชันยูทิลิตีขนาดเล็ก แทบทั้งหมด ทีมพัฒนาเขียนขึ้นเอง
- โมดูลขนาดกลาง หากใบอนุญาตเอื้ออำนวยก็จะ fork แล้วรวมไว้ในโค้ดเบส
- ไลบรารีขนาดใหญ่ (pdf.js, Mermaid, MathJax เป็นต้น) จะถูก รวมเข้ามาโดยตรึงไว้ที่เวอร์ชันที่ผ่านการตรวจสอบแล้ว และอัปเกรดให้น้อยที่สุด เฉพาะเมื่อพบประเด็นด้านความปลอดภัยที่สำคัญ
- การเปลี่ยนแปลงจากภายนอกทั้งหมดจะถูกตรวจทานอย่างละเอียดและผ่านขั้นตอนทดสอบอย่างเข้มงวด
- วิธีนี้ยังช่วย ลดจำนวน sub-dependency ทำให้ลดความเสี่ยงตั้งต้นที่โค้ดอันตรายจะเล็ดลอดเข้ามาได้
องค์ประกอบที่รวมอยู่ในแอปจริง
- ในแอปที่ผู้ใช้รันจริง จะมีเพียงแพ็กเกจไม่กี่ตัว เช่น Electron, CodeMirror, moment.js เท่านั้นที่ถูกรวมอยู่
- ส่วนเครื่องมือพัฒนาอื่น ๆ จะใช้เฉพาะในขั้นตอน build ของแอป และ จะไม่ถูกส่งต่อไปยังผู้ใช้ปลายทาง
การตรึงเวอร์ชันและการจัดการ lockfile
- dependency ภายนอกทั้งหมดจะถูกจัดการผ่าน การตรึงเวอร์ชัน (pin) อย่างเข้มงวด และ การ commit lockfile
- สิ่งนี้ช่วยให้การติดตั้ง ทำซ้ำได้อย่างสม่ำเสมอ และติดตามการเปลี่ยนแปลงได้ง่าย
- นโยบาย ไม่รันสคริปต์ postinstall ช่วยปิดกั้นความเป็นไปได้ของการรันโค้ดตามอำเภอใจระหว่างติดตั้งตั้งแต่ต้นทาง
กระบวนการอัปเดตที่ช้าและรอบคอบ
- เมื่อต้องอัปเกรด dependency จะมีขั้นตอน ตรวจทานอย่างเป็นระบบ ดังนี้
- ตรวจ changelog อย่างละเอียดทีละบรรทัด
- ตรวจสอบ dependency ย่อยที่ถูกเพิ่มเข้ามาในเวอร์ชันใหม่
- หากการเปลี่ยนแปลงมีขนาดใหญ่หรือมีปัจจัยเสี่ยง จะตรวจ diff กับซอร์ส upstream โดยตรง
- ทำการทดสอบทั้งแบบอัตโนมัติและแบบแมนนวลกับเส้นทางหลักและแพลตฟอร์มสำคัญ
- จะ commit lockfile ก็ต่อเมื่อผ่านทุกขั้นตอนข้างต้นแล้วเท่านั้น
- dependency ส่วนใหญ่ไม่จำเป็นต้องเปลี่ยนแปลงบ่อย จึงมีความถี่ในการอัปเดตต่ำโดยธรรมชาติ
- การนำโค้ดภายนอกใหม่เข้ามาจะถูก พิจารณาและจัดการในระดับเดียวกับการรับ dependency ใหม่
"เวลาเผื่อ" เพื่อความเสถียร: Time is a buffer
- การอัปเกรดต่าง ๆ จะ ไม่ถูกปล่อยใช้งานทันที แต่ถูกหน่วงเวลาไว้ช่วงหนึ่ง
- ในช่วงเวลานี้ ชุมชนโอเพนซอร์สและนักวิจัยด้านความปลอดภัยสามารถ ตรวจพบเวอร์ชันที่เป็นอันตราย ได้ล่วงหน้า
- เมื่อถึงเวลาปล่อยใช้งานจริง โอกาสที่ปัญหาจะถูกยืนยันแล้วมีสูงขึ้น จึง ช่วยลดความเสี่ยงได้อย่างมีประสิทธิภาพ
บทสรุป
- มาตรการด้านความปลอดภัยเพียงอย่างเดียวไม่สามารถ กำจัดความเสี่ยงจากการโจมตีซัพพลายเชน ได้ทั้งหมด
- อย่างไรก็ตาม Obsidian ใช้ การลด dependency, กราฟที่ตื้น, การตรึงเวอร์ชัน, การห้าม postinstall, และการอัปเดตแบบช้าโดยเน้นการตรวจทาน ร่วมกันเพื่อลดความเสี่ยงลงอย่างมาก
- กระบวนการเหล่านี้ยังช่วยเพิ่มเวลาสำหรับการตรวจพบความเสี่ยงก่อนที่โค้ดจะไปถึงมือผู้ใช้อย่างมีนัยสำคัญ
- แนวทางด้านความปลอดภัยทั้งหมดของ Obsidian และประวัติการตรวจสอบความปลอดภัยที่ผ่านมา ดูได้ที่ security page อย่างเป็นทางการ
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
หลายความเห็นมองข้ามไปว่าผู้ใช้ส่วนใหญ่ใช้ปลั๊กอินชุมชนของบุคคลที่สามใน Obsidian อยู่จริง ๆ โดยโมเดลความปลอดภัยของปลั๊กอิน Obsidian นั้นอ่อนแอมาก เพราะปลั๊กอินมีสิทธิ์เข้าถึงไฟล์ทั้งหมดใน vault หาก Obsidian พยายามใส่ฟีเจอร์เข้ามาเองให้มากกว่านี้ ความเสี่ยงด้านความปลอดภัยก็คงลดลงได้ หรือจะปรับเป็นโครงสร้างแบบส่วนขยายเบราว์เซอร์ ที่ระบุสิทธิ์ที่ปลั๊กอินใช้และบล็อกการเข้าถึงสิทธิ์ที่ไม่ได้รับอนุญาตก็ได้เช่นกัน ซึ่งทั้งหมดนี้ให้ความปลอดภัยที่ใช้งานได้จริงกับผู้ใช้มากกว่าแนวคิดว่า “พึ่งพาบุคคลที่สามน้อยกว่า” มาก
เมื่อก่อนเคยได้ยินคนในวงการซอฟต์แวร์บางคนพูดถึงกรณีที่ไอเดียจากการออกแบบวิดีโอเกมไหลเข้าสู่ซอฟต์แวร์ทั่วไปแบบอื่น ๆ แต่เดี๋ยวนี้แทบไม่ได้ยินแล้ว ถ้าคนแกนหลักจากบริษัทเกมรุ่นเก่าอย่าง Blizzard เขียนหนังสือสรุปอย่างละเอียดว่าระบบปลั๊กอินของ World of Warcraft ในช่วง 10 ปีแรกทำงานอย่างไร มีปัญหาอะไร และเสริมความปลอดภัยกันอย่างไร ก็น่าจะเป็นประโยชน์อย่างมากต่อระบบนิเวศซอฟต์แวร์โดยรวม ระบบปลั๊กอินของหลายโปรเจ็กต์ทุกวันนี้ยังปนเปกันระหว่างความหลวมกับความไม่เป็นมืออาชีพ
ปลั๊กอิน Obsidian เข้าถึงได้ไม่ใช่แค่ไฟล์ใน vault แต่รวมถึงไฟล์ทั้งหมดในคอมพิวเตอร์ด้วย เคยชี้ประเด็นนี้ใน Discord มาก่อน แต่ถูกเมิน
คิดว่าเหมือน Arch Linux อยู่พอสมควร แม้แต่วิศวกรเองก็ยังจัดการความปลอดภัยได้ยากเวลาต้องดูแลซอฟต์แวร์จาก AUR โดยตรง ดังนั้นการคาดหวังให้ผู้ใช้ทั่วไปเป็นคนรับผิดชอบเรื่องความปลอดภัยเองจึงเกินจริง
คิดว่าอีกไม่นานน่าจะมีกรณีข้อมูลรั่วจากปลั๊กอิน Obsidian ออกมาให้เห็น แล้วตอนนั้นทีมถึงจะเริ่มใส่มาตรการป้องกัน อย่างน้อยควรมีระบบ publisher ที่ผ่านการยืนยันตัวตน
กำลังพัฒนาปลั๊กอิน Obsidian ในเชิงพาณิชย์อยู่ และอยากให้มีขั้นตอนการตรวจสอบที่เข้มขึ้นสำหรับปลั๊กอินระดับสูงกว่าทั่วไป ถ้ามีทั้งคลังที่ชุมชนช่วยกันดูแลแบบ AUR ของ Arch Linux และอีกคลังที่ตรวจเข้มกว่า ก็น่าจะช่วยทั้งเรื่องความเร็วในการรีวิวปลั๊กอินและความปลอดภัย
มีคำอธิบายว่า “การโจมตี supply chain คือการซ่อนอัปเดตอันตรายไว้ในโค้ดโอเพนซอร์สที่หลายแอปใช้ร่วมกัน” แต่ไม่ว่า source code แบบไหนที่ไม่ใช่แค่ FOSS ก็เป็นเป้าการโจมตีได้เหมือนกัน ความคิดที่ว่า FOSS เปราะบางกว่าเสมอเป็นปัญหา
นโยบาย “ไม่รันสคริปต์ postinstall ระหว่างติดตั้ง” มีเจตนาดี แต่ถ้าแพ็กเกจถูกเจาะไปแล้ว การข้าม postinstall ก็ไม่ได้ทำให้โค้ดที่เหลือปลอดภัยขึ้น และถ้าแพ็กเกจปกติ postinstall ก็อาจช่วยให้ติดตั้งได้อย่างถูกต้องด้วย ในเหตุการณ์จริง ปัญหามักเกิดจากแพตช์ช่องโหว่ทั่วไปมากกว่าการโจมตี supply chain ดังนั้นการขวางการอัปเดตอาจยิ่งเพิ่มความเสี่ยง
ทุกวันนี้การสแกนความปลอดภัยมักทำหลังติดตั้ง (post install) การกันไม่ให้มีอะไรถูกรันระหว่างติดตั้งจึงเป็นเรื่องดี และหวังว่าในอนาคตจะมีฟีเจอร์สแกนหรือล็อกข้อจำกัดตั้งแต่ขั้นตอนติดตั้งมากขึ้น ปัจจุบันมีเครื่องมือเชิงพาณิชย์บางตัวทำได้ แต่ยังไม่แพร่หลาย
ถึงอย่างนั้นมันก็ยังมีประโยชน์ต่อการปกป้องเครื่อง build เพราะไม่ต้องกังวลว่าสคริปต์สุ่มจาก dependency จำนวนมากจะมาถูกรันบนเครื่องของฉัน
นักพัฒนาต้องรับผิดชอบต่อโค้ดทั้งหมดที่แจกจ่ายให้ผู้ใช้ ถ้าไม่ pin dependency ก็แทบไม่ต่างจาก “ดาวน์โหลดโค้ดสุ่มจากอินเทอร์เน็ตแล้วหวังดวง”
การ pin dependency อาจทำให้พลาดแพตช์ความปลอดภัยในภายหลัง ซึ่งก็เสี่ยงเช่นกัน ดังนั้นต้องมีระบบที่ทำให้รู้ตัวเมื่อมีแพตช์ความปลอดภัยใหม่ออกมา และต้องเลือกว่าจะ backport แพตช์นั้นหรืออัปเดตไปเวอร์ชันใหม่
ต่อให้ pin dependency ไว้ มันก็ยังมี dependency ของมันเองอีกอยู่ดี สุดท้ายก็ยังเป็นโครงสร้างแบบ “ดาวน์โหลดโค้ดสุ่มแล้วหวังเอา” อยู่เสมอ โดยเฉพาะแอปที่ใช้ Electron ซึ่งพ่วงโค้ดมหาศาลมาเลย
ช่วงนี้เจอคำแนะนำบ่อยว่า ต่อให้มี patch release ออกมาก็ไม่ควรอัปเดต dependency ซึ่งผมไม่ค่อยเข้าใจ ถ้าไม่อัปเดต ความเสี่ยงติดมัลแวร์อาจลดลงก็จริง แต่โดยปกติแพตช์ก็มักมีไว้เพื่อปรับปรุงความปลอดภัย เลยสงสัยว่าการไม่ใช้แพตช์ล่าสุดเป็นทางเลือกที่ฉลาดจริงหรือไม่
ประเด็นสำคัญคือการเข้าใจว่าทำไมถึงจะใช้แพตช์ และมันเปลี่ยนอะไรบ้าง เราไม่มีเวลาอ่าน source code ทุกบรรทัดกันอยู่แล้ว ก็เลยใช้เครื่องมือและบริการหลัก ๆ อย่าง Npm Audit เพื่อดูข้อมูลสรุปช่องโหว่ ผมใช้กลยุทธ์ชะลอการอัปเดตถ้าไม่จำเป็นจริง ๆ เพราะการอัปเดตเป็นทั้ง attack vector และเป็นสาเหตุหลักของบั๊กด้วย แต่ก็ต้องตรวจเป็นประจำว่าเราเปิดรับช่องโหว่อะไรอยู่บ้าง ถ้าเป็นช่องโหว่ในฟีเจอร์ที่ไม่ได้ใช้ บางครั้งก็เลื่อนการอัปเดตออกไป และจะรีบอัปเดตก็ต่อเมื่อเป็นปัญหาร้ายแรงจริง ๆ ความปลอดภัยเป็นกระบวนการเชิงรุกและต่อเนื่อง และการตอบสนองก็ควรขึ้นกับระดับความเสี่ยงที่องค์กรยอมรับได้ ไม่ใช่แค่ “อัปเดตเสมอ” หรือ “ไม่อัปเดตเด็ดขาด”
ช่วงนี้ทุกครั้งที่อัปเดต Z-WaveJS UI ก็มักมีการอัปเดต dependency ตามมาเรื่อย ๆ ด้วยรูปแบบที่ไม่น่าพอใจแบบนี้เลยต้องคอยตรวจเอง ทุกวันนี้เป็นยุคที่ทุกคนพึ่งพา dependency ของ dependency กันหมดแล้ว “ไม่มีวันจบ” แค่อัปเดตอัตโนมัติครั้งเดียวก็เสี่ยงได้
เวลาอัปเดตย่อมมีโอกาสทั้งดีขึ้นหรือแย่ลงเสมอ และใน ecosystem ของ npm (ซึ่ง Obsidian ก็อยู่ในนั้น) ความเสี่ยงยิ่งสูงกว่า npm ต้องอาศัยคนเข้ามาจัดการถอดแพ็กเกจอันตราย ทำให้การจัดการช้า การจงใจหน่วงการอัปเดตไว้บ้างจึงพอช่วยป้องกันได้
ช่วงหลังมานี้มีแนวโน้มที่จะรอสักหน่อยหลัง patch release ออก แล้วค่อยติดตั้ง เพราะปัจจุบันอุบัติเหตุมักถูกตรวจพบภายในไม่กี่ชั่วโมง หลายบริษัทก็คอยเฝ้าระวัง npm และทำธุรกิจด้านความปลอดภัยด้วย pnpm สามารถตั้งค่าให้ติดตั้งเฉพาะแพ็กเกจที่เผยแพร่มาแล้วเกิน X นาทีได้ ฉันมักรออย่างน้อย 24 ชั่วโมง การตั้งค่า minimumreleaseage ของ pnpm
การโจมตีที่แพ็กเกจของฉันเจอเมื่อ 2 สัปดาห์ก่อน เล็งไปที่ patch release ไม่ใช่สคริปต์ postinstall และเพราะระบบสแกนอัตโนมัติจับได้เร็ว ประเด็นนี้เลยไม่ได้ถูกพูดถึงมากเหมือนก่อน หากพบช่องโหว่ในแพ็กเกจ ก็จะมีการแจ้งเตือนทันทีและชัดเจน การใช้ version ranges ถือว่าแย่มากสำหรับการรับมือการโจมตี supply chain
ยากจะเห็นด้วยกับคำกล่าวที่ว่าแพ็กเกจไม่ได้ใหญ่ เพราะมีแค่ Electron, CodeMirror และ moment.js เท่านั้น Electron เป็นซอฟต์แวร์ที่ซับซ้อนมากเพราะสร้างบน webview ส่วน moment.js ก็มี API ที่ดีกว่าแทนได้แล้ว ระดับการจัดการ dependency ของ Obsidian จึงดูเป็นเพียงมาตรฐานขั้นต่ำ ไม่ได้รู้สึกว่าเป็นนโยบายความปลอดภัยที่โดดเด่นอะไรนัก ถึงอย่างนั้นการตรวจ security audit เป็นประจำก็ถือเป็นเรื่องดี
ปกติผมใช้แอปอื่นที่ไม่ใช่ Obsidian แต่มาเริ่มสนใจ Obsidian ขึ้นเรื่อย ๆ การที่มันเป็นแอป Electron ทำให้กินทรัพยากรเยอะและไม่ใช่ native เลยทำให้รู้สึกไม่ค่อยไว้ใจ JS อยู่เสมอ สงสัยว่าผมอาจจะหัวเก่าเกินไปหรือเปล่า
JavaScript เป็นภาษาที่ปลอดภัยมาก เว็บเบราว์เซอร์คือกรณีตัวอย่างที่ประสบความสำเร็จในการรัน JavaScript อย่างปลอดภัยในระดับโลก แต่ละเว็บไซต์ไม่สามารถอ่านข้อมูลของกันและกันได้ Electron เองก็ sandbox JavaScript ด้วยเอนจิน v8 ถ้าเลี่ยงการรันโค้ดจากอินพุตของผู้ใช้ ก็ถือว่าปลอดภัยมาก ปัญหาการโจมตี supply chain เป็นเรื่องของ npm เอง ไม่ใช่ปัญหาของภาษา JS และ npm มีหน้าที่ต้องใช้มาตรการความปลอดภัยที่เข้มงวดกว่านี้ตอนเผยแพร่แพ็กเกจ
ไม่ว่าจะวัดด้วยเกณฑ์ไหน JavaScript ก็เป็นหนึ่งในภาษาที่มีการใช้งานมากที่สุดในโลก รันได้แทบทุกคอมพิวเตอร์และสมาร์ตโฟน ยิ่งใช้เยอะก็ยิ่งพบประเด็นความปลอดภัยบ่อยขึ้น ไม่มีหลักฐานว่าแอป native จะปลอดภัยกว่าเสมอไป
แอป Electron ไม่ได้เป็นปัญหาอะไร GitHub, VS Code, Slack, Discord, Postman ล้วนสร้างบน Electron ทั้งนั้น ผมชอบถามเหมือนกันว่า ในแอปจดโน้ต Markdown มันจะมีงานอะไรที่ทำให้ประสิทธิภาพเป็นปัญหาจริง ๆ หรือคุณยังใช้โน้ตบุ๊กเก่ามาก ๆ เปิดผ่านเบราว์เซอร์ Lynx แบบข้อความล้วนอยู่กันแน่
ผมไม่ค่อยชอบ Electron เท่าไร (เลยชอบ tauri มากกว่า) แต่ตัว Obsidian เองยอดเยี่ยมมาก จึงไม่จำเป็นต้องเมินมันเพียงเพราะใช้ Electron แถมยังเอาไปเชื่อมกับ MCP เพื่อใช้เป็นคลังความรู้ส่วนตัวได้ดีด้วย แนะนำเลย
Electron หนักจริง บน PC อาจไม่ค่อยมีปัญหา แต่บนมือถือถ้ามีโน้ตเป็นพัน ๆ ไฟล์ แอปจะเปิดช้าแม้ไม่ใช้ปลั๊กอิน ผู้ใช้หลายคนเลยอ้อมไปใช้ปลั๊กอินหรือแอปสำหรับจดเร็วแทน แต่ก็ยังอยากเห็น Obsidian แบบ native ที่เบากว่านี้
Obsidian สร้างบน Electron และด้วยธรรมชาติของมันก็ย่อมมีทั้งความหนักและความเสี่ยงด้านความปลอดภัย
ใช้ Emacs กับ Org-Roam และรันใน VM ที่ไม่มีการเชื่อมต่อเครือข่าย (Qube ของ Qubes OS) ถึงอย่างนั้นก็ยังไม่สามารถตรวจโค้ดทั้งหมดที่รันอยู่ใน Emacs ได้ด้วยตัวเอง
ถ้าอยากได้แอป native และลดความเสี่ยงด้าน supply chain ให้ต่ำลงอีก ทางเลือกหนึ่งคือ Zim wiki ที่ใช้ GTK และมีแพ็กเกจในลินุกซ์ดิสโทรหลัก ๆ ไปที่ Zim wiki