หมวดหมู่: Security

RaceRace Condition: ภาวะแย่งชิงเงื่อนไขRaceRace Condition: ภาวะแย่งชิงเงื่อนไข

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

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


Race Condition คืออะไร?

ลองจินตนาการถึง “การแข่งขันวิ่งแข่ง” ของคำสั่งในโปรแกรม…

Race Condition เกิดขึ้นเมื่อระบบทำงานแบบขนานกัน (Concurrent/Parallel) และผลลัพธ์ของโปรแกรมขึ้นอยู่กับ “ลำดับก่อน-หลัง” หรือ “จังหวะเวลา (Timing)” ในการทำงานของกระบวนการเหล่านั้น

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


ตัวอย่างภาพชัด: ปัญหาเงินในบัญชี (Double-Spending)

เพื่อให้เห็นภาพง่ายที่สุด ลองดูสถานการณ์การถอนเงินผ่านระบบธนาคารดิจิทัล

สถานะเริ่มต้น

  • นาย A มีเงินในบัญชี 1,000 บาท
  • นาย A ส่งคำสั่งถอนเงิน 1,000 บาท พร้อมกัน 2 คำสั่งในเสี้ยววินาทีเดียวกัน (Request 1 และ Request 2)
[Request 1] ---> อ่านยอดเงิน (1,000) ----------------------> จ่ายเงิน 1,000 บาท ---> อัปเดตยอดคงเหลือ (0)
[Request 2] -------> อ่านยอดเงิน (1,000) ---> จ่ายเงิน 1,000 บาท ---> อัปเดตยอดคงเหลือ (0)

สิ่งที่เกิดขึ้นจริง (ความพังทลาย)

  1. Request 1 เช็กยอดเงิน: พบว่ามี 1,000 บาท (ผ่าน เงื่อนไขถูกต้อง)
  2. Request 2 เช็กยอดเงินในเสี้ยววินาทีเดียวกัน: พบว่ายังมี 1,000 บาท อยู่ (เพราะ Request 1 ยังไม่ได้ตัดเงิน)
  3. Request 1 ทำการอนุมัติให้ถอนเงิน 1,000 บาท
  4. Request 2 ทำการอนุมัติให้ถอนเงิน 1,000 บาท เช่นกัน!

ผลลัพธ์: นาย A สามารถถอนเงินได้รวม 2,000 บาท ทั้งที่มีเงินจริงแค่ 1,000 บาท และยอดเงินใน DB กลายเป็น 0 บาท (ขาดทุนไป 1,000 บาท)


ทำไม Race Condition ถึงน่ากลัว?

  • ตรวจจับยาก (Hard to Reproduce): เกิดขึ้นเฉพาะจังหวะที่ CPU หรือเครือข่ายประมวลผลพร้อมกันในระดับมิลลิวินาที ทำให้ตอนสั่ง Run/Test ตามปกติมักจะไม่เจอ
  • สร้างความเสียหายสูง: มักถูกนำไปใช้ในช่องโหว่ทางไซเบอร์ เช่น
    • Financial Loss: กดใช้โค้ดส่วนลด 1 ใบได้หลายครั้ง / ถอนเงินเกิน
    • Privilege Escalation: การสลับสิทธิ์การใช้งานของ User เพื่อเข้าถึงสิทธิ์ Admin
    • TOCTOU (Time-of-Check to Time-of-Use): เช็กไฟล์หรือสิทธิ์อย่างหนึ่ง แต่ตอนจะใช้งานจริงถูกสลับเปลี่ยนข้อมูลไปแล้ว

แนวทางการป้องกัน (How to Fix)

การแก้ปัญหา Race Condition คือการจัดการ Critical Section (โซนการทำงานที่ห้ามโดนแทรก) ให้ทำงานเป็นระเบียบเรียบร้อย

  1. การล็อกข้อมูล (Locking / Mutex)
    ใช้ตัวล็อกเพื่อบังคับให้ทำงานทีละ Request (Serial Processing)
    Pessimistic Locking: ล็อกแถวข้อมูลใน Database ทันทีที่อ่าน (เช่น ใช้ SELECT ... FOR UPDATE ใน SQL) เพื่อไม่ให้คำสั่งอื่นอ่านหรือแก้ไขได้จนกว่างานแรกจะเสร็จ
    Optimistic Locking: ใช้เวอร์ชันของข้อมูล (Version/Timestamp) เข้ามาเช็ก ถ้าจะบันทึกแล้วพบว่าเวอร์ชันเปลี่ยนไป ให้ยกเลิกคำสั่งนั้น
  2. ใช้ Atomic Operations
    เปลี่ยนคำสั่งที่ต้องทำหลายขั้นตอนให้กลายเป็น “คำสั่งรวดเดียวจบ” ที่ระดับ Database หรือ Memory เช่น
    SQL
    -- เสี่ยง Race Condition (เช็กก่อนแล้วค่อยลบ)
    -- IF balance >= 100 THEN UPDATE ...
    -- ปลอดภัย (Atomic Update)
    UPDATE accounts SET balance = balance - 100 WHERE id = 1 AND balance >= 100;
  3. คิวการทำงาน (Message Queue)
    จัดลำดับ Request ที่เข้ามาให้อยู่ในแถวคิว (First-In, First-Out) ผ่านระบบอย่าง RabbitMQ, Kafka หรือ Redis เพื่อประมวลผลทีละรายการแบบ Single-threaded

สรุป

Race Condition คือเรื่องของ “จังหวะเวลา” ในการเขียนโปรแกรม concurrent ระบบยิ่งใหญ่ ยิ่งประมวลผลเร็วเท่าไร ยิ่งมีโอกาสเกิดช่องโหว่นี้มากขึ้นเท่านั้น การคิดถึงสถานการณ์ที่อาจโดนส่ง Request มาพร้อมกันล่วงหน้า (Concurrency Design) จึงเป็นหัวใจสำคัญในการเขียนระบบที่ปลอดภัยครับ


อ่านเพิ่มเติม