Sandworm Lazarus แฟ้มคดี / CASE-020

CASE-020ระดับ 4 · ยืนแนวหน้าฝั่งป้องกันแนะนำให้อ่าน CASE-012 มาก่อน

Threat Modeling & เขียนโปรแกรมให้ปลอดภัย

Threat Modeling & Secure Development

ช่องโหว่ที่แก้ยากที่สุด มักเป็นช่องโหว่ที่ฝังมาตั้งแต่วันที่ยังวาดระบบบนกระดาน บทนี้จะชวนไปนั่งโต๊ะเดียวกับทีมที่สร้างแอปสั่งข้าวโรงอาหาร แล้วใช้คำถามสี่ข้อมองหาภัยก่อนเขียนโค้ดบรรทัดแรก ตั้งแต่วาดแผนภาพ ไล่หาภัยด้วย STRIDE จัดลำดับความเสี่ยง ไปจนถึงนิสัยเขียนโค้ดที่ปิดประตูใส่คนร้ายตั้งแต่ต้นทาง

เขียนและวาดภาพโดย Sandworm เวลาอ่าน ประมาณ 30 นาที ภาพอธิบาย 7 ภาพ อ่านจบแล้วจะ วาดแผนภาพและไล่หาภัยในแอปของตัวเองได้

สีในภาพประกอบของแฟ้มนี้: แดง ผู้โจมตีเหลือง จุดเสี่ยงเขียว การป้องกันฟ้า ข้อมูลของเรา

01ทำไมต้องคิดถึงภัย ตั้งแต่ยังไม่เขียนโค้ด Why threat modeling

ลองนึกภาพชมรมคอมพิวเตอร์ของโรงเรียนแห่งหนึ่ง เพิ่งปล่อยแอป “สั่งข้าวล่วงหน้า” ให้เพื่อน ๆ กดสั่งอาหารโรงอาหารจากห้องเรียน ไม่ต้องไปยืนต่อคิวยาวช่วงพักเที่ยงอีกแล้ว

สัปดาห์แรกทุกคนชอบมาก สัปดาห์ที่สอง แม่ค้าร้านข้าวมันไก่เริ่มเอะใจ ทำไมมีออเดอร์จำนวนหนึ่งจ่ายมาแค่จานละ 1 บาท ทีมพัฒนาไล่ดูอยู่ทั้งคืนจึงเจอว่า แอปบนมือถือเป็นคนบอกเซิร์ฟเวอร์ว่า “เมนูนี้ราคาเท่าไร” แล้วเซิร์ฟเวอร์ก็เชื่อตามนั้นทุกครั้ง ใครที่รู้วิธีแก้คำขอก่อนส่ง ก็ตั้งราคาเองได้เลย

ไม่มีใครเจาะระบบแบบในหนังสักคน โค้ดก็ไม่ได้เขียนผิดสักบรรทัด ทุกบรรทัดทำงานตรงตามที่ออกแบบไว้เป๊ะ ปัญหาคือ การออกแบบตั้งแต่ต้นไว้ใจสิ่งที่ไม่ควรไว้ใจ และนี่คือช่องโหว่ชนิดที่เครื่องมือสแกนโค้ดแทบหาไม่เจอ เพราะสำหรับเครื่อง โค้ดชุดนี้ “ถูกต้อง” ทุกอย่าง

โลกความปลอดภัยให้ความสำคัญกับเรื่องนี้มาก ถึงขั้นที่รายการความเสี่ยงของเว็บแอปที่คนทั่วโลกใช้อ้างอิง OWASP Top 10 ฉบับปี 2025 มีหมวดชื่อ Insecure Design (การออกแบบที่ไม่ปลอดภัย) แยกออกมาโดยเฉพาะ เพราะการออกแบบที่ผิดตั้งแต่ราก ต่อให้เขียนโค้ดเก่งแค่ไหนก็แก้ไม่ได้

การนั่งลงคิดอย่างเป็นระบบว่า “ระบบนี้พังได้อย่างไร” ตั้งแต่ตอนออกแบบ เรียกว่า Threat Modeling หรือการสร้างแบบจำลองภัยคุกคาม ฟังชื่อดูยาก แต่จริง ๆ คือสิ่งที่สถาปนิกเก่ง ๆ ทำมาตลอด ตอนวางผังบ้าน เขาคิดไว้แล้วว่าประตูหลังอยู่ตรงไหน หน้าต่างชั้นล่างต้องมีเหล็กดัดไหม กุญแจสำรองจะไม่ไปซุกไว้ใต้กระถางหน้าบ้าน ทำตอนนี้แค่ขีดเส้นบนกระดาษ ดีกว่ารอสร้างเสร็จแล้วค่อยมาทุบผนังติดกล้อง

ศัพท์น่ารู้: ภัยคุกคาม ช่องโหว่ และความเสี่ยง
ภัยคุกคาม (Threat) คือสิ่งไม่ดีที่อาจเกิดขึ้น เช่น “มีคนอยากกินข้าวฟรี” ช่องโหว่ (Vulnerability) คือจุดอ่อนที่ทำให้ภัยนั้นเป็นจริงได้ เช่น “เซิร์ฟเวอร์เชื่อราคาที่ส่งมาจากแอป” ส่วน ความเสี่ยง (Risk) คือการชั่งน้ำหนักว่าเรื่องนี้มีโอกาสเกิดแค่ไหน และถ้าเกิดแล้วเสียหายมากเท่าไร

บทนี้เราจะใช้แอปสั่งข้าวโรงอาหารเป็นตัวอย่างเดียวกันตั้งแต่ต้นจนจบ ไล่ไปทีละขั้นแบบที่ทีมพัฒนาจริงทำ เมื่ออ่านจบ เราจะหยิบวิธีเดียวกันไปใช้กับโปรเจกต์ของตัวเองได้ทันที ไม่ว่าจะเป็นแอปส่งการบ้าน บอตใน LINE หรือเว็บขายของของชมรม

02สี่คำถามที่ใช้ได้กับทุกระบบ The Four Questions

Threat Modeling มีหลายสำนัก หลายเครื่องมือ แต่แก่นของทุกสำนักสรุปได้ด้วยคำถามสี่ข้อ ที่ Adam Shostack ผู้เขียนหนังสือ Threat Modeling: Designing for Security เรียบเรียงไว้ และคนในวงการเรียกกันว่า Shostack’s Four Question Framework

FIG 01สี่คำถาม ที่วนกลับมาถามใหม่ทุกครั้งที่ระบบเปลี่ยน

บันทึกเป็นภาพ
แต่ละคำถามมี “ของที่ได้ออกมา” ชัดเจน: แผนภาพระบบ รายการภัย รายการงานที่ต้องแก้ และผลการทบทวน ถ้าทำครบสี่ข้อแล้วยังไม่มีของสี่อย่างนี้อยู่ในมือ แปลว่ายังข้ามขั้นอยู่
อ่านภาพนี้เป็นข้อความ
  1. เรากำลังทำอะไรอยู่ (What are we working on?): ได้แผนภาพการไหลของข้อมูล (DFD) ที่ทั้งทีมเข้าใจตรงกัน
  2. อะไรพังได้บ้าง (What can go wrong?): ได้รายการภัย จากการไล่ด้วย STRIDE หรือต้นไม้การโจมตี
  3. เราจะทำอะไรกับมัน (What are we going to do about it?): ได้รายการงานป้องกัน เลือกได้ว่าจะลด ตัดทิ้ง โอนย้าย หรือยอมรับความเสี่ยง
  4. เราทำได้ดีพอไหม (Did we do a good job?): ทบทวนว่าภัยทุกข้อมีเจ้าของและถูกทดสอบแล้ว

จากข้อ 4 วนกลับไปข้อ 1 ทุกครั้งที่ระบบเพิ่มฟีเจอร์หรือเปลี่ยนโครงสร้าง

สังเกตว่าคำถามใช้คำว่า “เรา” ไม่ใช่ “คุณ” Shostack อธิบายว่าตั้งใจเลือกคำนี้ เพราะ Threat Modeling เป็นงานที่ทำร่วมกันทั้งทีม ไม่ใช่การที่ผู้เชี่ยวชาญคนหนึ่งเดินมาตรวจแล้วชี้นิ้วสั่ง และใช้คำว่า “working on” แทน “building” เพื่อไม่ให้ติดภาพว่าต้องออกแบบให้เสร็จทั้งหมดก่อนค่อยเริ่มสร้าง ทีมที่ปล่อยงานทีละนิดก็ถามสี่ข้อนี้ได้ทุกรอบ

ลองเอาไปใช้กับแอปสั่งข้าวดู:

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

ฟังดูเรียบง่ายจนน่าสงสัยว่าทำไมต้องมีชื่อเรียก คำตอบคือคนส่วนใหญ่ไม่เคยถามข้อสองอย่างจริงจัง เราเคยชินกับการคิดว่าระบบ “ควรทำอะไร” จนลืมคิดว่ามัน “ถูกบังคับให้ทำอะไรได้บ้าง” ส่วนหัวข้อถัด ๆ ไปของบทนี้ ก็คือเครื่องมือที่ช่วยตอบแต่ละคำถามให้ได้ดีขึ้นนั่นเอง

03วาดแผนภาพการไหลของข้อมูล Data flow diagrams

คำถามแรก “เรากำลังทำอะไรอยู่” ตอบได้ดีที่สุดด้วยภาพ เครื่องมือยอดนิยมคือ แผนภาพการไหลของข้อมูล (Data Flow Diagram: DFD) ซึ่งไม่ได้สนใจว่าปุ่มสีอะไรหรือใช้ภาษาโปรแกรมอะไร สนใจแค่ว่าข้อมูลวิ่งจากไหนไปไหน ผ่านมือใครบ้าง

DFD มีตัวละครแค่ห้าแบบ จำง่ายมาก:

  • สิ่งภายนอก (External entity): คนหรือระบบที่เราควบคุมไม่ได้ เช่น นักเรียน แม่ค้า ระบบชำระเงินของธนาคาร วาดเป็นสี่เหลี่ยม
  • กระบวนการ (Process): โค้ดของเราที่รับข้อมูลมาทำอะไรบางอย่าง เช่น ระบบล็อกอิน API รับออเดอร์ วาดเป็นวงกลม
  • ที่เก็บข้อมูล (Data store): ฐานข้อมูล ไฟล์ แคช วาดเป็นเส้นคู่ขนานบนล่าง
  • เส้นทางข้อมูล (Data flow): ลูกศรที่บอกว่าข้อมูลอะไรวิ่งไปทางไหน
  • เส้นแบ่งความไว้ใจ (Trust boundary): เส้นประที่ขีดคั่นระหว่างโซนที่ไว้ใจกันได้ไม่เท่ากัน ตัวนี้สำคัญที่สุด

FIG 02แผนภาพการไหลของข้อมูลของแอปสั่งข้าวโรงอาหาร

บันทึกเป็นภาพ
เส้นประสีเหลืองคือเส้นแบ่งความไว้ใจ ทุกลูกศรที่วิ่งข้ามเส้นประ คือจุดที่ต้องตรวจให้หนักที่สุด เพราะข้อมูลกำลังเดินทางจากฝั่งที่เราควบคุมไม่ได้ เข้ามาในฝั่งที่เราต้องรับผิดชอบ
อ่านภาพนี้เป็นข้อความ

โซนที่ 1 อุปกรณ์ของผู้ใช้ (ควบคุมไม่ได้): นักเรียน และแท็บเล็ตของร้านค้า โซนที่ 2 เซิร์ฟเวอร์ของแอป: กระบวนการ “ระบบล็อกอิน” และ “API รับออเดอร์” โซนที่ 3 ฐานข้อมูลภายใน: “บัญชีผู้ใช้” และ “ออเดอร์” ด้านขวาของโซนเซิร์ฟเวอร์มีระบบชำระเงินของธนาคาร ซึ่งเป็นระบบภายนอกอยู่หลังเส้นแบ่งของตัวเอง

  1. นักเรียนส่งชื่อผู้ใช้และรหัสผ่านไปที่ระบบล็อกอิน
  2. ระบบล็อกอินตรวจกับตารางบัญชีผู้ใช้
  3. นักเรียนส่งรายการเมนูที่สั่งไปที่ API รับออเดอร์
  4. API บันทึกออเดอร์ลงฐานข้อมูล
  5. API ขอให้ระบบชำระเงินสร้าง QR สำหรับยอดนี้
  6. ระบบชำระเงินแจ้งกลับว่าจ่ายแล้ว
  7. API ส่งออเดอร์ที่จ่ายแล้วไปแสดงบนแท็บเล็ตของร้าน

ทำไมเส้นแบ่งความไว้ใจถึงสำคัญขนาดนั้น นึกถึง รปภ. หน้าคอนโด คนที่อยู่ข้างในตึกแล้ว รปภ. ไม่ต้องถามอะไรมาก แต่ทุกคนที่เดินผ่านประตูหน้าเข้ามา ต้องแลกบัตร ต้องบอกว่ามาหาห้องไหน เส้นแบ่งความไว้ใจก็คือประตูหน้าตึกในระบบของเรา ข้อมูลทุกชิ้นที่ข้ามเส้นนี้เข้ามา ต้องถูกตรวจใหม่หมด

จุดที่มือใหม่พลาดบ่อยที่สุดคือคิดว่า “แอปมือถือเป็นของเรา เพราะฉะนั้นไว้ใจได้” ไม่จริงเลย แอปอาจเป็นโค้ดที่เราเขียน แต่มันทำงานอยู่บนเครื่องของคนอื่น เจ้าของเครื่องจะดักแก้คำขอ ปลอมแอปขึ้นมาใหม่ หรือยิงคำขอตรง ๆ โดยไม่ผ่านแอปเลยก็ได้ ในแผนภาพ แอปมือถือจึงอยู่ฝั่ง “ควบคุมไม่ได้” เสมอ นี่แหละต้นเหตุของเรื่องข้าวมันไก่จานละบาท

ศัพท์น่ารู้: เส้นแบ่งความไว้ใจ (Trust boundary)
เส้นสมมติที่คั่นระหว่างสองบริเวณที่ไว้ใจกันได้ไม่เท่ากัน เช่น อินเทอร์เน็ตกับเซิร์ฟเวอร์ หรือเซิร์ฟเวอร์ของเรากับระบบของบริษัทอื่น กฎทอง: อะไรที่ข้ามเส้นเข้ามา ให้ถือว่าอาจถูกปลอมหรือถูกแก้มาแล้ว จนกว่าจะตรวจเอง

DFD ไม่ต้องสวย วาดบนไวต์บอร์ดหรือกระดาษ A4 ก็พอ ขอแค่ทั้งทีมดูแล้วเห็นภาพตรงกัน ถ้าระบบใหญ่ ให้วาดภาพรวมก่อน แล้วค่อยซูมลงไปวาดรายละเอียดเฉพาะส่วนที่สำคัญ เช่น ส่วนชำระเงิน

04ไล่หาภัยด้วย STRIDE Finding threats with STRIDE

มีแผนภาพแล้ว ก็ถึงคำถามที่สอง “อะไรพังได้บ้าง” ปัญหาคือ ถ้านั่งนึกเอาเอง สมองเรามักวนอยู่กับภัยสองสามแบบที่เคยได้ยิน แล้วพลาดที่เหลือไปหมด เครื่องมือที่ช่วยให้คิดได้ครบคือ STRIDE ซึ่ง Loren Kohnfelder และ Praerit Garg คิดขึ้นที่ Microsoft ตั้งแต่ปี 1999 และยังใช้กันแพร่หลายจนทุกวันนี้

STRIDE คือตัวอักษรตัวแรกของภัยหกประเภท แต่ละตัวคือการ “ทำลาย” คุณสมบัติด้านความปลอดภัยหนึ่งข้อ เวลาใช้ ให้หยิบองค์ประกอบในแผนภาพทีละชิ้น แล้วถามคำถามหกข้อกับมัน

FIG 03STRIDE กับแอปสั่งข้าว: ภัยหกแบบ และวิธีปิดแต่ละแบบ

บันทึกเป็นภาพ
อ่านทีละแถวจากซ้ายไปขวา: ชื่อภัยและคุณสมบัติที่มันทำลาย ตัวอย่างที่เกิดได้จริงในแอปสั่งข้าว (แดง) และสิ่งที่ปิดภัยนั้น (เขียว) สังเกตว่าวิธีแก้เกือบทั้งหมดเกิดขึ้นที่ “ฝั่งเซิร์ฟเวอร์” ซึ่งอยู่หลังเส้นแบ่งความไว้ใจ
อ่านภาพนี้เป็นข้อความ
  1. S: Spoofing ปลอมตัว (ทำลายการยืนยันตัวตน): ใช้รหัสผ่านเพื่อนล็อกอินสั่งข้าวให้บิลไปตกที่เพื่อน กันด้วยยืนยันตัวตนสองชั้นหรือพาสคีย์ และจำกัดจำนวนครั้งที่ลองรหัสผิด
  2. T (Tampering แก้ไขข้อมูล (ทำลายความถูกต้อง): แก้คำขอเปลี่ยนราคาจาก 45 บาทเป็น 1 บาท) กันด้วยการให้เซิร์ฟเวอร์คิดราคาเองจากเมนูในฐานข้อมูล
  3. R: Repudiation ปฏิเสธว่าไม่ได้ทำ (ทำลายการพิสูจน์ย้อนหลัง): นักเรียนอ้างว่าไม่ได้สั่ง ร้านอ้างว่าไม่ได้รับเงิน กันด้วยบันทึกทุกออเดอร์และการจ่ายเงินไว้ในที่ที่แก้ไขไม่ได้
  4. I: Information disclosure ข้อมูลรั่ว (ทำลายความลับ): เปลี่ยนเลขออเดอร์ในลิงก์แล้วเห็นเบอร์โทรของคนอื่น กันด้วยการตรวจทุกครั้งว่าออเดอร์เป็นของคนที่ขอจริง
  5. D: Denial of service ทำให้ใช้ไม่ได้ (ทำลายความพร้อมใช้งาน): ยิงออเดอร์ปลอมนับพันรายการช่วงพักเที่ยง กันด้วยการจำกัดอัตราคำขอและจำนวนออเดอร์ค้างจ่าย
  6. E: Elevation of privilege ยกระดับสิทธิ์ (ทำลายการกำหนดสิทธิ์): บัญชีนักเรียนเรียกหน้าแอดมินได้ตรง ๆ กันด้วยการตรวจบทบาทที่เซิร์ฟเวอร์ทุกคำขอ

เคล็ดลับคือไม่ต้องถามครบหกข้อกับทุกกล่องเสมอไป แนวทางที่นิยมใช้กันเรียกว่า STRIDE-per-element บอกว่าองค์ประกอบแต่ละแบบในแผนภาพ มักเจอภัยกลุ่มไหน

องค์ประกอบแต่ละแบบใน DFD มักเจอภัย STRIDE ข้อไหน
องค์ประกอบภัยที่ต้องถามก่อนในแอปสั่งข้าว ถามว่า…
สิ่งภายนอกExternal entityS, Rคนที่อ้างว่าเป็นนักเรียนคนนี้ เป็นตัวจริงไหม ถ้าเขาปฏิเสธทีหลัง เรามีหลักฐานไหม
กระบวนการProcessS, T, R, I, D, E ครบทั้งหกAPI รับออเดอร์ถูกหลอก ถูกยิงจนล่ม หรือถูกสั่งให้ทำเกินสิทธิ์ได้ไหม
ที่เก็บข้อมูลData storeT, I, D (และ R ถ้าเป็นที่เก็บล็อก)ใครแก้ตารางออเดอร์ได้บ้าง ถ้าฐานข้อมูลหลุด เบอร์โทรนักเรียนจะรั่วไหม
เส้นทางข้อมูลData flowT, I, Dข้อมูลระหว่างแอปกับเซิร์ฟเวอร์เข้ารหัสด้วย HTTPS ไหม มีใครดักแก้กลางทางได้ไหม

อย่าตกใจถ้าไล่เสร็จแล้วได้ภัยออกมาเป็นสิบ ๆ ข้อ นั่นคือสัญญาณว่าทำถูกแล้ว ในโลกจริงทีมพัฒนาบางทีมทำให้กระบวนการนี้สนุกขึ้นด้วยเกมไพ่ชื่อ Elevation of Privilege ที่แต่ละใบเขียนภัยหนึ่งแบบไว้ แล้วผลัดกันลงไพ่ว่าภัยใบนี้เกิดกับระบบของเราได้ตรงไหน

ศัพท์น่ารู้: IDOR (Insecure Direct Object Reference)
ภัยข้อ I ในภาพที่เปลี่ยนเลขในลิงก์แล้วเห็นของคนอื่น มีชื่อเฉพาะว่า IDOR คือระบบให้เราอ้างถึงข้อมูลด้วยเลขตรง ๆ แต่ลืมตรวจว่าข้อมูลชิ้นนั้นเป็นของเราหรือเปล่า เป็นหนึ่งในรูปแบบของ Broken Access Control ซึ่งอยู่อันดับ 1 ของ OWASP Top 10 ฉบับปี 2025

05ต้นไม้การโจมตี: คิดแบบคนร้ายทีละกิ่ง Attack trees

STRIDE ช่วยให้คิดได้ “กว้าง” ครบทุกประเภท แต่บางครั้งเราอยากคิดให้ “ลึก” ว่าคนร้ายจะไปถึงเป้าหมายหนึ่งได้กี่เส้นทาง และเส้นไหนง่ายที่สุด เครื่องมือสำหรับงานนี้คือ ต้นไม้การโจมตี (Attack tree) ที่ Bruce Schneier เผยแพร่ในนิตยสาร Dr. Dobb’s Journal เมื่อเดือนธันวาคม 1999

หลักการง่ายมาก ยอดของต้นไม้คือ “เป้าหมายของคนร้าย” กิ่งที่แตกลงมาคือวิธีที่จะไปถึงเป้าหมายนั้น และกิ่งแต่ละกิ่งก็แตกต่อไปได้อีกเรื่อย ๆ กิ่งที่แตกออกมามีสองแบบ:

  • กิ่งแบบ OR (หรือ): ทำวิธีไหนก็ได้สักวิธี ก็ไปถึงเป้าหมายแล้ว
  • กิ่งแบบ AND (และ): ต้องทำสำเร็จ “ทุกขั้น” ในกลุ่มนั้น จึงจะไปต่อได้

FIG 04ต้นไม้การโจมตี: “กินข้าวฟรี โดยไม่ต้องจ่ายเงิน”

บันทึกเป็นภาพ
เส้นทาง A และ B “ง่าย” เพราะไม่ต้องขโมยอะไรจากใครเลย จึงต้องปิดก่อน ส่วนเส้นทาง C เป็นกิ่งแบบ AND คนร้ายต้องได้ทั้งรหัสผ่านและผ่านด่านยืนยันชั้นที่สอง แค่เราตัดขั้นใดขั้นหนึ่ง ทั้งกิ่งก็ใช้ไม่ได้
อ่านภาพนี้เป็นข้อความ

เป้าหมาย: กินข้าวฟรีโดยไม่ต้องจ่ายเงิน แตกเป็นสามทางแบบ OR

  1. A. ปลอมสลิปโอนเงินให้ร้านดู (ง่าย): ตัดได้ด้วยการให้ระบบยืนยันยอดจากธนาคารโดยตรง ร้านดูสถานะในระบบเท่านั้น
  2. B. แก้ราคาในคำขอก่อนส่ง (ง่าย ถ้าเซิร์ฟเวอร์เชื่อราคาจากแอป): ตัดได้ด้วยการคิดราคาที่เซิร์ฟเวอร์
  3. C. ใช้บัญชีเพื่อนจ่ายแทน แบบ AND ต้องทำครบสองขั้น: C1 ได้รหัสผ่านของเพื่อน (ปานกลาง: เดา แอบดู หรือหน้าล็อกอินปลอม) และ C2 ผ่านด่านยืนยันชั้นที่สอง (ยาก) ตัดได้ด้วยการเปิดยืนยันตัวตนสองชั้นหรือพาสคีย์

พลังที่แท้จริงของต้นไม้การโจมตีอยู่ที่การ “ให้ค่า” กับใบไม้แต่ละใบ Schneier ยกตัวอย่างการติดป้ายว่า “ทำได้” หรือ “ทำไม่ได้” และการใส่ต้นทุนที่คนร้ายต้องจ่าย จากนั้นคำนวณขึ้นไปหายอด กิ่งแบบ OR ใช้ค่าของลูกที่ถูกที่สุด เพราะคนร้ายเลือกทางที่ถูกที่สุดได้ ส่วนกิ่งแบบ AND ต้องเอาต้นทุนของลูกทุกตัวมารวมกัน

คิดแบบนี้แล้วเห็นอะไรบางอย่างที่สำคัญมาก: คนร้ายไม่จำเป็นต้องเก่ง แค่เลือกทางที่ถูกที่สุด ระบบที่ล็อกประตูหน้าด้วยกุญแจห้าชั้น แต่ลืมปิดหน้าต่างห้องน้ำ ก็ไม่ได้ปลอดภัยกว่าบ้านที่ไม่ล็อกอะไรเลยสักเท่าไร หน้าที่ของฝั่งป้องกันจึงเป็นการไล่ปิด “ทางที่ถูกที่สุด” ไปเรื่อย ๆ จนทางที่เหลืออยู่แพงเกินกว่าใครจะอยากลอง

ในภาพนี้ เส้นทาง A กับ B ถูกและง่ายที่สุด เพราะไม่ต้องขโมยรหัสผ่านใคร ไม่ต้องมีทักษะพิเศษ ทั้งสองทางจึงต้องปิดก่อนเปิดแอปให้ใช้จริง ส่วนเส้นทาง C แม้ขั้น C1 จะเกิดได้ แต่การเปิดยืนยันสองชั้นทำให้ขั้น C2 แพงขึ้นมาก และเพราะเป็นกิ่งแบบ AND ทั้งเส้นทางจึงแพงตามไปด้วย

06จัดลำดับความเสี่ยง: แก้อะไรก่อนดี Rating risk

ถึงตอนนี้ ทีมแอปสั่งข้าวมีรายการภัยยาวเหยียด แต่เวลามีจำกัด จะแก้ทุกข้อพร้อมกันไม่ได้ คำถามที่สาม “เราจะทำอะไรกับมัน” จึงเริ่มจากการจัดลำดับ วิธีพื้นฐานที่สุดคือการชั่งสองอย่าง โอกาสเกิด (Likelihood) และ ผลกระทบถ้าเกิดขึ้น (Impact) แล้ววางภัยแต่ละข้อลงบนตาราง

FIG 05ตารางความเสี่ยง: โอกาสเกิด × ผลกระทบ

บันทึกเป็นภาพ
ตำแหน่งในตารางเป็นการประเมินของทีมสมมติทีมนี้ ไม่ใช่สูตรตายตัว ทีมอื่นที่มีบริบทต่างกันอาจวางต่างออกไปได้ สิ่งสำคัญคือทั้งทีมคุยกันจนเห็นตรงกัน แล้วลงมือจากมุมขวาบนก่อน
อ่านภาพนี้เป็นข้อความ
  1. I ข้อมูลรั่ว (เปลี่ยนเลขออเดอร์แล้วเห็นของคนอื่น): โอกาสสูง ผลกระทบสูง: แก้ก่อนเป็นอันดับแรก
  2. T แก้ราคา: โอกาสสูง ผลกระทบปานกลาง: แก้ก่อนเปิดใช้
  3. E เข้าหน้าแอดมิน: โอกาสปานกลาง ผลกระทบสูง แก้ก่อนเปิดใช้
  4. S ใช้บัญชีเพื่อน: โอกาสปานกลาง ผลกระทบปานกลาง วางแผนแก้
  5. D ยิงออเดอร์ปลอม: โอกาสปานกลาง ผลกระทบปานกลาง วางแผนแก้
  6. R ปฏิเสธว่าไม่ได้สั่ง: โอกาสปานกลาง ผลกระทบต่ำ ทำบันทึกล็อกไว้ แล้วติดตาม

ตารางสามคูณสามแบบนี้หยาบก็จริง แต่ได้ผลดีมากกับทีมเล็ก ถ้าอยากละเอียดขึ้น มีวิธีที่เป็นระบบกว่าให้เลือก เช่น OWASP Risk Rating Methodology ที่แตกโอกาสเกิดออกเป็นปัจจัยย่อย (คนร้ายเก่งแค่ไหน มีแรงจูงใจไหม ช่องโหว่หาเจอง่ายไหม) และแตกผลกระทบออกเป็นด้านเทคนิคกับด้านธุรกิจ ส่วนช่องโหว่ที่ถูกเปิดเผยต่อสาธารณะแล้ว วงการใช้คะแนนมาตรฐานชื่อ CVSS เพื่อบอกความรุนแรงเป็นตัวเลขที่เทียบกันได้ทั่วโลก

เมื่อรู้ลำดับแล้ว ภัยแต่ละข้อมีทางเลือกให้ทำได้สี่แบบ:

  1. ลดความเสี่ยง (Mitigate)

    ทางที่ใช้บ่อยที่สุด เพิ่มการป้องกันจนภัยเกิดยากขึ้นหรือเสียหายน้อยลง เช่น ตรวจสิทธิ์ทุกครั้งที่มีคนขอดูออเดอร์

  2. ตัดทิ้ง (Eliminate)

    ถ้าฟีเจอร์ไหนเสี่ยงเกินคุ้ม ก็ไม่ต้องทำมัน เช่น ไม่ต้องเก็บเบอร์โทรนักเรียนเลย ถ้าไม่ได้ใช้ ข้อมูลที่ไม่มี ก็รั่วไม่ได้

  3. โอนย้าย (Transfer)

    ให้คนที่เก่งเรื่องนั้นกว่ารับไปดูแล เช่น ไม่เก็บข้อมูลบัตรหรือบัญชีธนาคารเอง ให้ระบบชำระเงินของธนาคารจัดการ แล้วเรารับแค่ผลว่าจ่ายแล้ว

  4. ยอมรับ (Accept): แต่ต้องตั้งใจ

    บางภัยเล็กจนไม่คุ้มแก้ ยอมรับได้ แต่ต้องเขียนบันทึกไว้ชัด ๆ ว่าใครตัดสินใจ เพราะอะไร และจะกลับมาดูอีกเมื่อไร การ “ลืม” ไม่ใช่การยอมรับ

07ย้ายความปลอดภัยไปไว้ต้นทาง Secure SDLC & shift-left

ซอฟต์แวร์ทุกตัวเดินผ่านวงจรคล้าย ๆ กัน ตั้งแต่เก็บความต้องการ ออกแบบ เขียนโค้ด ทดสอบ ปล่อยใช้งาน ไปจนถึงดูแลระบบ เรียกว่า วงจรพัฒนาซอฟต์แวร์ (Software Development Life Cycle: SDLC) สมัยก่อนหลายทีมเอาเรื่องความปลอดภัยไปไว้ท้ายสุด เขียนเสร็จแล้วค่อยจ้างคนมาเจาะทดสอบก่อนเปิดตัวสองสัปดาห์ แล้วก็มักเจอปัญหาใหญ่ระดับที่ต้องรื้อออกแบบใหม่ ทั้งที่ไม่มีเวลาเหลือแล้ว

แนวคิด Shift-left คือการย้ายงานความปลอดภัยไปทาง “ซ้าย” ของเส้นเวลา ให้เริ่มตั้งแต่วันแรก ๆ และฝังอยู่ในทุกขั้น เหมือนการตรวจสุขภาพประจำปีที่เจอโรคตั้งแต่เริ่มต้น ย่อมรักษาง่ายกว่ารอให้อาการหนักแล้วค่อยไปโรงพยาบาล

FIG 06วงจรพัฒนาซอฟต์แวร์ที่มีความปลอดภัยอยู่ในทุกขั้น

บันทึกเป็นภาพ
แถบสีแดงที่ยาวขึ้นเรื่อย ๆ เป็นภาพเปรียบเทียบ ไม่ใช่ตัวเลขจริง: ปัญหาเดียวกัน ยิ่งเจอช้า ยิ่งต้องรื้อของที่สร้างทับไว้เยอะ จึงยิ่งแก้ยากและแพง การ shift-left คือพยายามจับให้ได้ตั้งแต่ช่องซ้าย ๆ
อ่านภาพนี้เป็นข้อความ
  1. วางความต้องการ: เขียนความต้องการด้านความปลอดภัยไว้ตั้งแต่ต้น เช่น “ออเดอร์ต้องดูได้เฉพาะเจ้าของ”
  2. ออกแบบ ทำ Threat Modeling: วาด DFD ไล่ STRIDE จัดลำดับความเสี่ยง
  3. เขียนโค้ด: ใช้แนวทางเขียนโค้ดปลอดภัย และให้เพื่อนรีวิวโค้ดทุกครั้ง
  4. ทดสอบ: สแกนอัตโนมัติด้วย SAST, SCA และ DAST และเขียนเทสต์สำหรับภัยที่พบ
  5. ปล่อยใช้งาน: ตรวจการตั้งค่า สแกนหาความลับที่หลุดอยู่ในโค้ด
  6. ดูแลระบบ: เฝ้าระวัง อัปเดตแพตช์ และมีช่องทางรับแจ้งช่องโหว่

ยิ่งพบปัญหาในขั้นหลัง ๆ การแก้ยิ่งยากและแพงขึ้น

ไม่ต้องคิดเองทั้งหมด มีกรอบแนวทางที่องค์กรทั่วโลกใช้อ้างอิงอยู่แล้ว ตัวที่สำคัญคือ Secure Software Development Framework (SSDF) ของ NIST สหรัฐฯ หรือเอกสาร SP 800-218 ฉบับ 1.1 ออกเมื่อกุมภาพันธ์ 2022 (และมีร่างฉบับ 1.2 เผยแพร่ช่วงปลายปี 2025) SSDF แบ่งงานเป็นสี่กลุ่ม:

  • PO: Prepare the Organization เตรียมองค์กร: กำหนดบทบาท ฝึกคน เตรียมเครื่องมือ
  • PS: Protect the Software ปกป้องตัวซอฟต์แวร์: เก็บโค้ดให้ปลอดภัย ตรวจสอบได้ว่าไฟล์ที่ปล่อยออกไปไม่ถูกแก้
  • PW: Produce Well-Secured Software ผลิตซอฟต์แวร์ที่ปลอดภัย: ออกแบบโดยคำนึงถึงภัย รีวิวโค้ด ทดสอบ
  • RV: Respond to Vulnerabilities รับมือช่องโหว่: รับแจ้ง วิเคราะห์ แก้ และหาต้นตอเพื่อไม่ให้เกิดซ้ำ

อีกแนวทางหนึ่งที่เก่าแก่และมีอิทธิพลมากคือ Security Development Lifecycle (SDL) ของ Microsoft ซึ่งเป็นบ้านเกิดของ STRIDE นั่นเอง สำหรับนักเรียนหรือทีมเล็ก ไม่ต้องทำครบทุกข้อตั้งแต่วันแรก เริ่มจากสามอย่างที่คุ้มที่สุดก่อน: ทำ Threat Modeling ตอนออกแบบ รีวิวโค้ดทุกครั้งก่อนรวมเข้าโค้ดหลัก และเปิดเครื่องมือสแกนอัตโนมัติที่ฟรีอยู่แล้ว

08นิสัยเขียนโค้ดที่ปิดประตูให้คนร้าย Secure coding basics

Threat Modeling บอกว่าต้องปิดประตูบานไหน ส่วนการเขียนโค้ดอย่างปลอดภัยคือการปิดประตูบานนั้นให้สนิทจริง หัวข้อนี้รวมนิสัยห้าข้อที่ป้องกันปัญหาได้มากที่สุดต่อแรงที่ลงไป ตัวอย่างโค้ดเป็น JavaScript ฝั่งเซิร์ฟเวอร์ แต่หลักการใช้ได้กับทุกภาษา

1. ความลับต้องไม่อยู่ในโค้ด

รหัสผ่านฐานข้อมูล กุญแจ API ของระบบชำระเงิน โทเค็นของบอต สิ่งเหล่านี้เรียกรวม ๆ ว่า ความลับ (Secrets) ความผิดพลาดยอดฮิตคือพิมพ์มันลงไปในโค้ดตรง ๆ เพราะสะดวก แล้ววันหนึ่งโค้ดก็ถูก push ขึ้น GitHub ถูกส่งต่อให้เพื่อน หรือติดไปในไฟล์ที่แจกจ่าย

ไม่ควรทำ: ฝังความลับไว้ในโค้ดconfig.js
// ใครเห็นไฟล์นี้ ก็ได้กุญแจไปด้วย และมันจะค้างอยู่ในประวัติ git ตลอดไป
const DB_PASSWORD   = "Kaprao#2026";
const PAYMENT_KEY   = "pay_live_9f3c...e21a";
const db = connect({ user: "canteen", password: DB_PASSWORD });
ควรทำ: ดึงความลับจากภายนอกตอนรันโปรแกรมconfig.js
// ค่าจริงอยู่ใน Secret Manager ของคลาวด์ หรือตัวแปรสภาพแวดล้อมบนเซิร์ฟเวอร์
// ไฟล์ .env สำหรับเครื่องตัวเอง ต้องอยู่ใน .gitignore เสมอ
const DB_PASSWORD = process.env.DB_PASSWORD;
const PAYMENT_KEY = await secrets.get("payment-key");
if (!DB_PASSWORD) throw new Error("missing DB_PASSWORD");

จุดที่หลายคนไม่รู้: ต่อให้ลบความลับออกจากไฟล์ในคอมมิตถัดไป มันก็ยังอยู่ใน ประวัติของ git ใครโคลนโปรเจกต์ไปก็ย้อนดูได้ ถ้าความลับหลุดไปแล้ว ทางเดียวที่ปลอดภัยคือ เปลี่ยนความลับนั้นใหม่ทันที (rotate) แล้วค่อยตามล้างประวัติ

2. อย่าต่อข้อความเป็นคำสั่งฐานข้อมูล

ถ้าเอาข้อความที่ผู้ใช้พิมพ์มาต่อเข้ากับคำสั่ง SQL ตรง ๆ ผู้ใช้ก็มีโอกาส “เขียนคำสั่งเพิ่ม” แทรกเข้ามาได้ นี่คือ SQL Injection ที่เราเจอใน CASE-012 วิธีแก้มาตรฐานคือ Parameterized query ส่งคำสั่งกับข้อมูลแยกกันไปคนละช่อง ฐานข้อมูลจะไม่มีวันเอาข้อมูลไปตีความเป็นคำสั่ง

ไม่ควรทำ: ต่อข้อความเป็นคำสั่งorders.js
const sql = "SELECT * FROM orders WHERE student_id = '" + req.query.id + "'";
db.query(sql);
ควรทำ: ส่งข้อมูลแยกจากคำสั่งorders.js
db.query("SELECT * FROM orders WHERE student_id = $1", [req.user.id]);
// และใช้ id จากบัญชีที่ล็อกอินอยู่ ไม่ใช่จากค่าที่ผู้ใช้ส่งมา (กัน IDOR ไปพร้อมกัน)

3. แปลงข้อความก่อนแสดงผลเสมอ

ถ้าแอปให้นักเรียนเขียนรีวิวเมนูได้ แล้วเราเอาข้อความนั้นไปแสดงในหน้าเว็บแบบดิบ ๆ คนร้ายก็พิมพ์โค้ดสคริปต์ลงไปในรีวิว ให้มันไปทำงานบนเบราว์เซอร์ของทุกคนที่เปิดอ่าน นี่คือ Cross-site Scripting (XSS) วิธีกันคือ Output encoding แปลงอักขระพิเศษให้กลายเป็นข้อความธรรมดาก่อนแสดงผล

ไม่ควรทำ: ยัดข้อความผู้ใช้ลงหน้าเว็บตรง ๆreview.js
reviewBox.innerHTML = review.text;   // ถ้าในรีวิวมีแท็กสคริปต์ มันจะทำงานทันที
ควรทำ: ให้เบราว์เซอร์ถือว่าเป็นข้อความเท่านั้นreview.js
reviewBox.textContent = review.text;  // แสดงเป็นตัวอักษร ไม่ถูกตีความเป็นโค้ด
// ถ้าใช้ระบบเทมเพลตหรือเฟรมเวิร์ก ให้ใช้โหมด escape อัตโนมัติที่มันมีให้

4. อย่าเชื่อสิ่งที่มาจากฝั่งผู้ใช้

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

ไม่ควรทำ: ใช้ราคาที่แอปส่งมาcheckout.js
const total = req.body.items.reduce((sum, it) => sum + it.price * it.qty, 0);
ควรทำ: ตรวจข้อมูลเข้า แล้วคิดราคาจากฐานข้อมูลเองcheckout.js
for (const it of req.body.items) {
  if (!Number.isInteger(it.qty) || it.qty < 1 || it.qty > 10) return res.status(400).end();
}
const menu  = await db.menuPrices(req.body.items.map(it => it.menuId));
const total = req.body.items.reduce((sum, it) => sum + menu[it.menuId] * it.qty, 0);

5. ให้สิทธิ์น้อยที่สุด และพังอย่างสุภาพ

บัญชีที่แอปใช้ต่อฐานข้อมูล ไม่ควรมีสิทธิ์ลบตารางหรือสร้างผู้ใช้ใหม่ ถ้าโค้ดถูกเจาะ ความเสียหายจะถูกจำกัดอยู่แค่สิทธิ์ที่บัญชีนั้นมี หลักนี้เรียกว่า สิทธิ์น้อยที่สุดเท่าที่จำเป็น (Least privilege) และเวลาเกิดข้อผิดพลาด ให้ตอบผู้ใช้สั้น ๆ ว่าเกิดปัญหา ส่วนรายละเอียดอย่างข้อความ error ของฐานข้อมูลหรือเส้นทางไฟล์ในเซิร์ฟเวอร์ ให้เก็บลงล็อกฝั่งเรา อย่าโชว์ให้คนนอกเห็น OWASP Top 10 ฉบับปี 2025 ถึงกับเพิ่มหมวดใหม่ชื่อ Mishandling of Exceptional Conditions ให้เรื่องการจัดการข้อผิดพลาดที่ไม่ดีโดยเฉพาะ

09ตาคู่ที่สอง และเครื่องมืออัตโนมัติ Code review, SAST, DAST & SCA

คนเขียนโค้ดมักมองไม่เห็นข้อผิดพลาดของตัวเอง เหมือนเขียนเรียงความแล้วอ่านทวนเองกี่รอบก็ไม่เห็นคำผิด แต่เพื่อนอ่านทีเดียวเจอ การรีวิวโค้ด (Code review) จึงเป็นด่านป้องกันที่ถูกและได้ผลที่สุดด่านหนึ่ง ทีมที่ดีตั้งกติกาว่าโค้ดทุกชิ้นต้องมีอีกคนอ่านและกดอนุมัติก่อนรวมเข้าโค้ดหลักเสมอ

เวลารีวิวเรื่องความปลอดภัย ไม่ต้องอ่านแบบงมเข็ม ให้ถือคำถามติดมือไว้ไม่กี่ข้อ:

  • โค้ดนี้รับข้อมูลที่ข้ามเส้นแบ่งความไว้ใจไหม ถ้ารับ ตรวจแล้วหรือยัง
  • มีการต่อข้อความเป็นคำสั่ง (SQL คำสั่งระบบ) หรือแสดงข้อความผู้ใช้แบบดิบ ๆ ไหม
  • ตรวจสิทธิ์ทุกครั้งไหม ว่าคนที่ขอ มีสิทธิ์กับข้อมูลชิ้นนี้จริง
  • มีความลับ คีย์ หรือรหัสผ่านหลุดเข้ามาในโค้ดหรือไฟล์ตั้งค่าไหม
  • ถ้าเกิด error ระบบตอบอะไรออกไป และบันทึกอะไรไว้

ส่วนงานที่น่าเบื่อและต้องทำซ้ำทุกวัน ให้เครื่องมืออัตโนมัติทำแทน เครื่องมือหลักมีสามสี่ตระกูล แต่ละตัวมองแอปจากคนละมุม และเก่งกันคนละเรื่อง

FIG 07เครื่องมือแต่ละตัว มองแอปจากมุมไหน

บันทึกเป็นภาพ
แอปหนึ่งตัวมีสามชั้นให้ตรวจ: โค้ดที่เราเขียนเอง ไลบรารีที่ยืมคนอื่นมา และตัวแอปที่กำลังทำงานจริง ไม่มีเครื่องมือตัวไหนเห็นครบทั้งสามชั้น ทีมที่ดีจึงใช้หลายตัวประกอบกัน และยังต้องมีคนรีวิวอยู่ดี
อ่านภาพนี้เป็นข้อความ
  1. SAST (Static Application Security Testing) อ่านซอร์สโค้ดที่เราเขียน โดยไม่ต้องรันโปรแกรม หาเจอตั้งแต่ตอนเขียน ชี้ได้ถึงบรรทัด แต่มีผลบวกลวงบ้าง
  2. SCA (Software Composition Analysis) หรือการสแกนไลบรารี อ่านรายชื่อไลบรารีและเวอร์ชันที่แอปใช้ แล้วเทียบกับฐานข้อมูลช่องโหว่ที่ประกาศแล้ว
  3. การสแกนหาความลับ (Secret scanning) ค้นหากุญแจและรหัสผ่านที่หลุดเข้าไปในโค้ดหรือประวัติ git
  4. DAST (Dynamic Application Security Testing) ยิงคำขอเข้าไปที่แอปที่กำลังรันจริงจากภายนอก แบบเดียวกับที่คนร้ายทำ เห็นปัญหาการตั้งค่าและพฤติกรรมจริง แต่ชี้ไม่ได้ว่าผิดที่บรรทัดไหน

เรื่องไลบรารีสำคัญกว่าที่หลายคนคิด แอปสมัยนี้มีโค้ดที่เราเขียนเองเป็นส่วนน้อย ที่เหลือคือไลบรารีโอเพนซอร์สที่ดึงมาใช้ต่อกันเป็นทอด ๆ ถ้าไลบรารีตัวไหนมีช่องโหว่ แอปของเราก็มีช่องโหว่นั้นไปด้วยโดยไม่ได้เขียนผิดสักบรรทัด OWASP Top 10 ฉบับปี 2025 จึงยกหมวด Software Supply Chain Failures ขึ้นมาไว้อันดับ 3 (เรื่องนี้เจาะลึกใน CASE-015)

ข่าวดีคือเครื่องมือหลายตัวฟรีสำหรับโปรเจกต์นักเรียน ตัวอย่างเช่น GitHub เปิด push protection ให้คลังโค้ดสาธารณะใช้ฟรีตั้งแต่ต้นปี 2024 ระบบจะตรวจตอนเรา push ถ้าเจออะไรที่หน้าตาเหมือนกุญแจหรือโทเค็นของบริการที่รู้จัก ก็จะบล็อกไว้ก่อนขึ้นไปถึงที่สาธารณะ แพลตฟอร์มเก็บโค้ดเจ้าหลัก ๆ ก็มีระบบแจ้งเตือนเมื่อไลบรารีที่เราใช้มีช่องโหว่ เปิดไว้แล้วอย่าเพิกเฉยต่อคำเตือนก็พอ

ความเชื่อ

“เปิดเครื่องมือสแกนครบทุกตัวแล้ว ไม่เจออะไรเลย แปลว่าแอปปลอดภัยแล้ว”

ความจริง

เครื่องมือเก่งเรื่องรูปแบบที่รู้จักอยู่แล้ว เช่น SQL ที่ต่อข้อความ หรือไลบรารีที่มีช่องโหว่ประกาศแล้ว แต่ไม่เข้าใจ “ตรรกะทางธุรกิจ” ของเรา มันไม่รู้ว่าราคาข้าวต้องมาจากเซิร์ฟเวอร์ หรือนักเรียนไม่ควรเห็นออเดอร์ของคนอื่น ช่องโหว่แบบนี้หาเจอได้ด้วย Threat Modeling และคนที่รีวิว

Security Champions: ทหารพรานประจำทีม

องค์กรส่วนใหญ่มีผู้เชี่ยวชาญด้านความปลอดภัยน้อยกว่านักพัฒนาหลายสิบเท่า ให้ทีมความปลอดภัยไปนั่งรีวิวทุกอย่างเองไม่มีทางทัน หลายองค์กรจึงตั้ง Security Champion คือนักพัฒนาในแต่ละทีมที่สนใจเรื่องนี้ ได้รับการฝึกเพิ่ม แล้วทำหน้าที่เป็นคนแรกที่เพื่อนในทีมนึกถึงเวลามีคำถามเรื่องความปลอดภัย ชวนทีมทำ Threat Modeling ตอนเริ่มฟีเจอร์ใหม่ และเป็นสะพานเชื่อมกับทีมความปลอดภัยกลาง

จุดสำคัญคือ Champion ไม่ใช่ “ตำรวจ” ที่คอยจับผิดหรือกดปุ่มอนุญาต แต่เป็นเพื่อนร่วมทีมที่ช่วยให้คนอื่นตัดสินใจเองได้ดีขึ้น ในชมรมหรือห้องเรียน เราเริ่มแบบนี้ได้ทันที: ให้หนึ่งคนในกลุ่มรับบทถาม “อะไรพังได้บ้าง” ในทุกการประชุมโปรเจกต์ แล้วผลัดกันรับบทนี้ ทุกคนจะค่อย ๆ ติดนิสัยคิดแบบคนร้าย เพื่อจะป้องกันได้ดีขึ้น

10สรุปให้จำ Recap

ความปลอดภัยของซอฟต์แวร์ไม่ได้เริ่มที่โค้ด มันเริ่มที่คำถามว่า “อะไรพังได้บ้าง” ตั้งแต่วันที่ยังวาดระบบอยู่บนกระดาน ถ้าจำได้แค่หกข้อ ให้จำหกข้อนี้

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

ลองทบทวนดู

แอปมือถือเป็นโค้ดที่ทีมเราเขียนเอง ทำไมในแผนภาพ DFD ยังต้องวางไว้นอกเส้นแบ่งความไว้ใจ

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

นักเรียนคนหนึ่งเปลี่ยนเลขในลิงก์ /orders/1043 เป็น 1044 แล้วเห็นออเดอร์ของเพื่อน นี่คือภัยตัวไหนใน STRIDE และแก้อย่างไร

ตัว I Information disclosure (ข้อมูลรั่ว) รูปแบบนี้เรียกว่า IDOR แก้ด้วยการให้เซิร์ฟเวอร์ตรวจทุกครั้งว่าออเดอร์ที่ขอเป็นของบัญชีที่ล็อกอินอยู่จริง ไม่ใช่แค่เช็กว่าล็อกอินแล้ว

ในต้นไม้การโจมตี ถ้ากิ่งหนึ่งเป็นแบบ AND มีสองขั้น เราต้องปิดทั้งสองขั้นไหม

ไม่จำเป็น กิ่งแบบ AND ต้องสำเร็จทุกขั้นคนร้ายจึงไปต่อได้ แค่ตัดขั้นใดขั้นหนึ่งได้ ทั้งกิ่งก็ใช้ไม่ได้แล้ว เช่น ต่อให้คนร้ายได้รหัสผ่านเพื่อนไป ถ้าผ่านด่านยืนยันชั้นที่สองไม่ได้ ก็จ่ายเงินแทนเพื่อนไม่ได้

เผลอ push กุญแจ API ขึ้น GitHub แล้วรีบลบออกในคอมมิตถัดไป ถือว่าจบเรื่องไหม

ไม่จบ กุญแจยังอยู่ในประวัติของ git และอาจถูกคนหรือบอตเก็บไปแล้วตั้งแต่วินาทีแรก สิ่งที่ต้องทำทันทีคือเปลี่ยนกุญแจใหม่ (rotate) แล้วตรวจดูว่ามีการใช้กุญแจเก่าผิดปกติไหม จากนั้นจึงล้างประวัติ และย้ายความลับไปไว้ใน Secret Manager หรือตัวแปรสภาพแวดล้อม

SAST กับ DAST ต่างกันอย่างไร

SAST อ่านซอร์สโค้ดโดยไม่ต้องรันโปรแกรม เจอปัญหาได้เร็วตั้งแต่ตอนเขียนและชี้ได้ถึงบรรทัด ส่วน DAST ยิงคำขอเข้าไปที่แอปที่กำลังรันจริงจากภายนอก เห็นปัญหาการตั้งค่าและพฤติกรรมจริงแบบที่คนร้ายเห็น แต่บอกไม่ได้ว่าผิดที่บรรทัดไหน ทั้งสองแบบเสริมกัน