
แกะรอยช่องโหว่ยอดฮิตใน GraphQL API ที่นักพัฒนาต้องรู้
โลกของการพัฒนาแอปพลิเคชันสมัยใหม่เติบโตไปพร้อมกับเทคโนโลยี API (Application Programming Interface) หนึ่งในตัวช่วยที่ได้รับความนิยมอย่างมากคือ GraphQL เพราะช่วยให้การดึงข้อมูลจากเซิร์ฟเวอร์มีประสิทธิภาพสูง ผู้ใช้งานสามารถระบุข้อมูลที่ต้องการได้อย่างแม่นยำ ทำให้ลดการดึงข้อมูลที่ไม่จำเป็นและประหยัดแบนด์วิดท์ลงได้มาก
อย่างไรก็ตาม แม้ GraphQL จะมีข้อดีมากมาย แต่ก็มีช่องโหว่ที่พบบ่อยและเป็นอันตรายหากนักพัฒนาไม่ได้ใส่ใจในการออกแบบและป้องกันที่เพียงพอ ซึ่งช่องโหว่นี้มักจะเกี่ยวข้องกับแนวคิดที่เรียกว่า IDOR (Insecure Direct Object Reference) และ Mass Assignment
GraphQL คืออะไร ทำไมถึงได้รับความนิยม
GraphQL เป็นภาษาสำหรับการ Query ข้อมูลสำหรับ API ของเรา และเป็น Runtime สำหรับการ Execute Query เหล่านั้นด้วยข้อมูลที่เรามีอยู่ ข้อดีหลักๆ คือการที่ผู้ใช้งานสามารถ กำหนดโครงสร้างข้อมูล ที่ต้องการได้เอง ทำให้ได้ข้อมูลที่ “ตรงใจ” ไม่มากเกินไป ไม่น้อยเกินไป และสามารถดึงข้อมูลจากหลายๆ แหล่งได้ในการร้องขอเพียงครั้งเดียว
ความยืดหยุ่นนี้ทำให้ GraphQL เป็นตัวเลือกที่น่าสนใจสำหรับแอปพลิเคชันที่ต้องการประสิทธิภาพและความสามารถในการปรับขนาด โดยเฉพาะอย่างยิ่งในยุคของ Microservices และ Mobile Application
ช่องโหว่ยอดฮิตใน GraphQL: IDOR และ Mass Assignment
ช่องโหว่ที่มักจะถูกพบใน GraphQL API คือการที่ผู้ไม่ประสงค์ดีสามารถเข้าถึงหรือแก้ไขข้อมูลที่ไม่ได้เป็นของตนเองได้ โดยอาศัยการอ้างอิงถึง ID ของวัตถุ นั้นๆ โดยตรง หรือที่เรียกว่า IDOR
และมักจะมาคู่กับการโจมตีแบบ Mass Assignment ซึ่งเกิดจากการที่ระบบเปิดโอกาสให้ผู้ใช้งานส่งข้อมูลเพื่ออัปเดตหลายๆ ฟิลด์พร้อมกัน โดยไม่ได้ตรวจสอบอย่างเข้มงวดว่าผู้ใช้งานมีสิทธิ์ในการแก้ไขฟิลด์เหล่านั้นหรือไม่ ทำให้เกิดความเสี่ยง เช่น ผู้ใช้งานทั่วไปสามารถเปลี่ยนสถานะตัวเองให้เป็นผู้ดูแลระบบได้
กลไกที่ทำให้เกิดช่องโหว่
ปัญหานี้มักเกิดจากการออกแบบ Schema ของ GraphQL ที่ไม่รัดกุมนัก โดยเฉพาะเมื่อนักพัฒนาใช้ Input Type เดียวกัน สำหรับทั้งการสร้างข้อมูลใหม่ (Create) และการอัปเดตข้อมูล (Update)
สมมติว่ามี UserInput ที่ใช้สำหรับสร้างผู้ใช้ และ UserUpdateInput สำหรับอัปเดตข้อมูล หาก Input ทั้งสองแบบมีฟิลด์ role หรือ isAdmin รวมอยู่ด้วย และระบบไม่ได้ทำการ ตรวจสอบสิทธิ์ (Authorization Check) อย่างเข้มงวดก่อนการดำเนินการใดๆ ผู้ใช้งานที่มีเจตนาร้ายอาจจะส่งค่า id ของผู้ใช้อื่น พร้อมกับค่า role: "admin" เพื่อทำการแก้ไขสิทธิ์ของผู้ใช้นั้นได้ทันที หรือแม้แต่แก้ไขสิทธิ์ของตนเอง
การที่ระบบไม่ได้ กรอง (Filter) ฟิลด์ที่รับเข้ามาอย่างละเอียดว่าฟิลด์ใดบ้างที่ผู้ใช้งานแต่ละคนมีสิทธิ์แก้ไขได้ ทำให้กลายเป็นช่องทางให้เกิดการโจมตีได้ง่ายๆ
ป้องกันช่องโหว่นี้ได้อย่างไร
การป้องกันช่องโหว่นี้ทำได้ไม่ยาก แต่ต้องอาศัยความใส่ใจในการออกแบบตั้งแต่เริ่มต้น
ประการแรก แยก Input Type ออกจากกัน ควรมี Input Type สำหรับการสร้างข้อมูลโดยเฉพาะที่ไม่มีฟิลด์ id หรือฟิลด์ที่ sensitive อย่าง role และมี Input Type สำหรับการอัปเดตข้อมูลที่แตกต่างออกไป โดยอาจจะไม่มีฟิลด์ id เลย และจำกัดฟิลด์ที่สามารถอัปเดตได้
ประการที่สอง ใช้การตรวจสอบสิทธิ์ (Authorization) อย่างเข้มงวด ทุกครั้งที่มีการดำเนินการแก้ไขข้อมูล จะต้องมีการตรวจสอบสิทธิ์ของผู้ใช้งานที่ร้องขออย่างละเอียดว่ามีสิทธิ์ในการแก้ไขข้อมูลนั้นๆ และฟิลด์เหล่านั้นจริงหรือไม่ อย่าเชื่อถือข้อมูลที่ส่งมาจากฝั่งไคลเอนต์เพียงอย่างเดียว
ประการที่สาม ใช้ White List สำหรับฟิลด์ที่สามารถอัปเดตได้ แทนที่จะใช้วิธี Black List ที่เป็นการบล็อกฟิลด์ที่ไม่ต้องการ ให้ใช้วิธีการกำหนดว่าฟิลด์ใดบ้างที่อนุญาตให้อัปเดตได้ วิธีนี้จะช่วยป้องกันการอัปเดตฟิลด์ที่ไม่พึงประสงค์ได้อย่างมีประสิทธิภาพมากกว่า
การออกแบบ GraphQL API ที่ปลอดภัยต้องอาศัยความเข้าใจในกลไกของมันและการป้องกันที่เป็นระบบ เพราะความยืดหยุ่นและความทรงพลังของ GraphQL หากไม่ได้รับการจัดการอย่างเหมาะสม ก็อาจกลายเป็นช่องทางให้เกิดความเสี่ยงด้านความปลอดภัยได้ง่ายๆ