สวัสดีครับคุณผู้อ่านทุกท่าน วันนี้เราจะมาพูดถึงหัวข้อที่น่าสนใจเกี่ยวกับ GraphQL ที่กำลังได้รับความนิยมในวงการพัฒนาซอฟต์แวร์เป็นอย่างมาก แต่ในความสะดวกและทรงพลังของมัน ก็มีจุดที่ต้องระวังเช่นกันครับ โดยหัวข้อที่เราจะพาไปเจาะลึกในวันนี้มี 2 ส่วนหลัก ได้แก่
- โหมด Introspection ใน GraphQL — ฟีเจอร์ที่ช่วยให้นักพัฒนาสามารถตรวจสอบโครงสร้าง Schema ของระบบได้ แต่ในขณะเดียวกัน หากใช้งานอย่างไม่ระมัดระวัง อาจนำไปสู่ความเสี่ยงด้านความปลอดภัย
- การใช้ Aliases ใน GraphQL — ฟีเจอร์ที่ช่วยเพิ่มความยืดหยุ่นในการดึงข้อมูล แต่สามารถถูกนำไปใช้ในรูปแบบที่สร้างปัญหาได้ เช่น การเลี่ยงผ่านข้อจำกัดของระบบ หรือโจมตีด้วย Batching Brute-Force
สำหรับบทความนี้จะพาไปสำรวจทั้ง 2 หัวข้อ พร้อมตัวอย่างและวิธีป้องกัน เพื่อให้สามารถใช้ GraphQL ได้อย่างปลอดภัยและมีประสิทธิภาพมากขึ้น
GraphQL คืออะไร?
ก่อนที่จะเข้าสู่หัวข้อหลัก เรามาทำความรู้จัก GraphQL กันก่อน
GraphQL เป็นภาษาสำหรับการดึงข้อมูล (Query Language) ที่ออกแบบมาสำหรับ API โดยเฉพาะ พัฒนาโดย Facebook และเปิดตัวครั้งแรกในปี 2015 จุดเด่นของ GraphQL คือความสามารถในการให้ผู้ใช้งานระบุข้อมูลที่ต้องการได้อย่างแม่นยำ ส่งผลให้การรับส่งข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์เป็นไปอย่างมีประสิทธิภาพมากขึ้น
ในทางปฏิบัติ GraphQL ทำงานผ่าน schema ซึ่งเป็นการกำหนดโครงสร้างข้อมูลที่ API จะสนับสนุน นักพัฒนาสามารถเขียน query เพื่อดึงข้อมูลที่ต้องการจากเซิร์ฟเวอร์ได้โดยตรง โดยไม่จำเป็นต้องพึ่ง endpoints หลายจุดเหมือน REST API
วัตถุประสงค์ของ GraphQL
GraphQL ถูกออกแบบมาเพื่อแก้ไขปัญหาที่เกิดขึ้นจากการใช้ REST API แบบดั้งเดิม เช่น การดึงข้อมูลที่มากเกินความจำเป็น (Over-fetching) หรือการดึงข้อมูลที่ไม่เพียงพอ (Under-fetching) วัตถุประสงค์หลักของ GraphQL มีดังนี้
- ลดความซับซ้อนของการจัดการข้อมูล: นักพัฒนาสามารถดึงข้อมูลที่ต้องการได้ในคำขอเดียว
- เพิ่มประสิทธิภาพการสื่อสาร: ลดจำนวนการร้องขอที่ไม่จำเป็นไปยังเซิร์ฟเวอร์
- สนับสนุนการขยายตัวของระบบ: GraphQL สามารถรวม API หลายชุดเข้าด้วยกันเพื่อให้บริการข้อมูลจากแหล่งเดียว
ข้อดีของ GraphQL เมื่อเทียบกับ REST API
- ยืดหยุ่นและปรับแต่งได้: นักพัฒนาสามารถระบุได้ว่าต้องการข้อมูลใดและไม่ต้องการข้อมูลใด ส่งผลให้ลดปริมาณข้อมูลที่ถูกส่งกลับ
- ลด Over-fetching และ Under-fetching: REST API มักให้ข้อมูลที่มากเกินไปหรือไม่เพียงพอต่อการใช้งาน GraphQL ช่วยแก้ปัญหานี้ได้โดยให้ไคลเอนต์กำหนดข้อมูลที่ต้องการเอง
- รวมข้อมูลจากหลายแหล่ง: GraphQL สามารถรวมข้อมูลจากหลายเซิร์ฟเวอร์หรือฐานข้อมูลได้อย่างง่ายดาย
- ปรับปรุงการพัฒนาไคลเอนต์: ด้วย GraphQL นักพัฒนาสามารถพัฒนาไคลเอนต์ที่สามารถตอบสนองต่อข้อมูลได้อย่างรวดเร็วและแม่นยำ
แม้ว่า GraphQL จะมีข้อดีที่โดดเด่น แต่ก็มีข้อเสีย โดยเฉพาะในเรื่องความซับซ้อนของการตั้งค่าที่ต้องการความเชี่ยวชาญเพิ่มเติมเช่น โหมด Introspection อีกทั้งผู้ใช้งานยังจำเป็นต้องเรียนรู้แนวคิดและเครื่องมือเพื่อใช้งาน GraphQL ได้อย่างได้อย่างมีความปลอดภัย ซึ่งถัดไปเราจะมาพูดถึงกัน
Introspection ใน GraphQL คืออะไร?
Introspection ใน GraphQL คือคุณสมบัติที่ช่วยให้ผู้ใช้งานสามารถตรวจสอบโครงสร้างของ Schema ได้โดยตรงจากเซิร์ฟเวอร์ ซึ่งหมายความว่าสามารถดูได้ว่ามี Query, Mutation, หรือ Subscription อะไรบ้าง รวมถึงชนิดข้อมูล (types) และฟิลด์ต่าง ๆ ที่เซิร์ฟเวอร์รองรับ
การ Introspect Schema ทำได้โดยการส่ง Query แบบพิเศษ เช่น
query {
__schema {
types {
name
fields {
name
}
}
}
}
ตัวอย่างผลลัพธ์
{
"data": {
"__schema": {
"types": [
{
"name": "Query",
"fields": [
{ "name": "getUser" },
{ "name": "listProducts" }
]
},
{
"name": "Mutation",
"fields": [
{ "name": "createUser" },
{ "name": "deleteProduct" }
]
}
]
}
}
}
คำขอด้านบนจะดึงโครงสร้างของ Schema ออกมา ซึ่งมีประโยชน์มากสำหรับการพัฒนาและการดีบัก แต่ในขณะเดียวกันก็อาจมีความเสี่ยงด้านความปลอดภัยหากเปิดใช้งานในระบบที่ต้องการความปลอดภัยสูง
วิธีป้องกันความเสี่ยงจาก Introspection
แนวทางที่แนะนำคือการปิดหรือจำกัดการเข้าถึงโหมด Introspection เพื่อป้องกันไม่ให้ผู้โจมตีสามารถดึงข้อมูลโครงสร้าง Schema ของ API ได้ วิธีนี้สามารถทำได้โดยการตั้งค่าให้ Introspection เปิดใช้งานเฉพาะในสภาพแวดล้อมที่เหมาะสม เช่น Development หรือจำกัดการเข้าถึงให้เฉพาะผู้ที่ได้รับอนุญาตเท่านั้น นอกจากนี้ ควรหลีกเลี่ยงการใช้ Regular Expressions (Regex) เพื่อบล็อกคำขอ Introspection เนื่องจากมีความเสี่ยงที่ผู้โจมตีจะใช้เทคนิคต่างๆ เช่น การปรับเปลี่ยน Query หรือการใช้ Alias เพื่อหลีกเลี่ยงการตรวจจับ การตั้งค่าที่เหมาะสมในเซิร์ฟเวอร์เพื่อควบคุมคำขอ Introspection จึงเป็นทางเลือกที่ปลอดภัยและมีประสิทธิภาพมากกว่า
Aliases ใน GraphQL คืออะไร
Alias ใน GraphQL เป็นฟีเจอร์ที่ช่วยให้ผู้ใช้สามารถเปลี่ยนชื่อฟิลด์ที่ร้องขอได้ในผลลัพธ์ การใช้งานนี้ช่วยให้สามารถร้องขอฟิลด์เดียวกันซ้ำได้ใน Query เดียว โดยแต่ละครั้งสามารถตั้งชื่อแตกต่างกันได้
ตัวอย่างการใช้งาน Alias
query {
firstUser: user(id: "1") {
name
}
secondUser: user(id: "2") {
name
}
}
ผลลัพธ์
{
"data": {
"firstUser": { "name": "John" },
"secondUser": { "name": "Jane" }
}
}
การใช้ Aliases ใน GraphQL มอบความยืดหยุ่นให้กับการเขียน Query โดยช่วยให้นักพัฒนาสามารถเปลี่ยนชื่อฟิลด์ในผลลัพธ์ได้ตามต้องการ ทำให้ Query อ่านง่ายและชัดเจนยิ่งขึ้น นอกจากนี้ ฟีเจอร์นี้ยังรองรับการร้องขอข้อมูลที่ซับซ้อน โดยสามารถดึงข้อมูลจากฟิลด์เดียวกันในหลายรูปแบบพร้อมกันได้ เช่น การเรียกข้อมูลผู้ใช้หลายคนในคำขอเดียว
อย่างไรก็ตาม ความสามารถนี้อาจกลายเป็นช่องโหว่ด้านความปลอดภัยได้ หากไม่มีการตั้งค่าหรือมาตรการป้องกันที่เหมาะสม ผู้โจมตีอาจใช้ Aliases เพื่อหลีกเลี่ยงการจำกัดจำนวนคำขอ (Rate Limits) หรือเพื่อเพิ่มภาระให้กับเซิร์ฟเวอร์โดยการส่ง Query ที่ซับซ้อนและซ้ำซ้อนในคำขอเดียว ดังนั้น การจัดการและควบคุมการใช้ Aliases จึงเป็นสิ่งสำคัญในการป้องกันการโจมตีที่อาจเกิดขึ้นจากฟีเจอร์นี้ในระบบ GraphQL
ตัวอย่างการโจมตีที่เกิดขึ้นจากการใช้ Aliases
Batching Brute-Force
Batching Brute-Force คือการรวมคำขอหลายรายการใน Query เดียว เพื่อทดลองค่าหลาย ๆ ค่าในฟิลด์ที่อาจเป็นช่องโหว่ เช่น รหัสผ่าน หรือ Token การทำเช่นนี้ช่วยให้ผู้โจมตีลดจำนวนคำขอที่ต้องส่งไปยังเซิร์ฟเวอร์ และเพิ่มโอกาสในการโจมตีสำเร็จ
ตัวอย่างการใช้ Batching Brute-Force
query {
login1: login(username: "admin", password: "password1") { token }
login2: login(username: "admin", password: "password2") { token }
login3: login(username: "admin", password: "password3") { token }
}
การโจมตีแบบ Batching Brute-Force จะบังคับให้เซิร์ฟเวอร์ต้องประมวลผลฟิลด์จำนวนมากพร้อมกัน หากไม่มีการตั้งค่าควบคุมที่เหมาะสม การโจมตีรูปแบบนี้สามารถทำให้เซิร์ฟเวอร์ตอบสนองช้าลงอย่างมาก หรือในกรณีที่เลวร้ายที่สุด อาจทำให้ระบบหยุดทำงาน (Crash) ได้ ส่งผลกระทบต่อประสิทธิภาพและการให้บริการของระบบ
Bypassing Rate Limits Using Aliases
ในระบบที่มีการตั้งค่า Rate Limit เพื่อจำกัดจำนวนคำขอที่ส่งจากผู้ใช้หรือ IP Address แต่ละครั้ง ผู้โจมตีสามารถหลีกเลี่ยงข้อจำกัดเหล่านี้ได้โดยใช้ Alias เพื่อเปลี่ยนชื่อฟิลด์ใน Query เดียว และรวมคำขอหลายรายการไว้ในนั้น
ตัวอย่าง Query ที่ใช้ Alias เพื่อ Bypass Rate Limits
query {
loginAlias1: login(username: "admin", password: "password1") { token }
loginAlias2: login(username: "admin", password: "password2") { token }
loginAlias3: login(username: "admin", password: "password3") { token }
}
ใน Query ข้างต้น แต่ละคำขอใช้ Alias เพื่อเปลี่ยนชื่อฟิลด์ เช่น loginAlias1, loginAlias2 เป็นต้น ทำให้ระบบ Rate Limit ที่ตรวจจับคำขอแบบทั่วไปไม่สามารถแยกคำขอที่ซ่อนอยู่ใน Query เดียวกันออกได้ ส่งผลให้ผู้โจมตีสามารถส่งคำขอจำนวนมากโดยไม่ถูกตรวจจับหรือบล็อก
วิธีป้องกัน
หนึ่งในวิธีที่ดีที่สุดในการจัดการกับปัญหานี้ คือการจำกัดจำนวน Alias ที่สามารถใช้ใน Query เดียว เพราะการใช้ Alias จำนวนมากเกินไปจะเพิ่มภาระให้เซิร์ฟเวอร์อย่างไม่จำเป็น การตั้งค่าขีดจำกัดนี้สามารถช่วยลดโอกาสที่คำขอที่ซับซ้อนจะเข้าสู่ระบบได้
นอกจากนี้ การประเมินความซับซ้อนของ Query ด้วยการตั้งค่า Query Complexity Limit ก็เป็นอีกหนึ่งกลยุทธ์ที่มีประสิทธิภาพ ด้วยการคำนวณค่าความซับซ้อนของ Query ตามจำนวนฟิลด์, Alias, และลำดับชั้นของข้อมูล การกำหนดขีดจำกัดนี้ช่วยป้องกันไม่ให้คำขอที่ซับซ้อนเกินไปถูกประมวลผล ซึ่งสามารถตั้งค่าได้ในระดับเซิร์ฟเวอร์ เช่น การใช้เครื่องมือ GraphQL Complexity Rules ใน Apollo Server ,อีกวิธีหนึ่งที่สำคัญคือการตรวจจับพฤติกรรมคำขอที่ผิดปกติ เช่น คำขอที่มี Alias ซ้ำกันจำนวนมากหรือคำขอที่ส่งมาจาก IP Address เดียวซ้ำๆ ระบบควรมีการวิเคราะห์ Logs เพื่อตรวจสอบพฤติกรรมของผู้ใช้งานและระบุการกระทำที่เข้าข่ายการโจมตี สรุป ก็คือ config ให้ดีนั่นแหละครับ
สุดท้ายนี้ การตั้งค่า Rate Limit ที่เหมาะสมถือเป็นรากฐานสำคัญ ระบบควรตรวจสอบคำขอแบบรวม เช่น คำขอที่มีหลาย Alias หรือคำขอที่มีโครงสร้างซับซ้อน เพื่อให้สามารถบล็อกคำขอที่มีความเสี่ยงได้อย่างมีประสิทธิภาพ
โดยสรุป
GraphQL นั้นเป็นเครื่องมือที่ช่วยให้งานพัฒนาราบรื่นขึ้นจริงๆ แต่ความยืดหยุ่นที่มันมอบให้ก็ต้องแลกมากับการดูแลด้านความปลอดภัยที่มากขึ้นนิดหน่อย หากเราใส่ใจและตั้งค่าต่างๆ อย่างเหมาะสม เช่น ปิด Introspection ใน Production หรือกำหนดขอบเขตการใช้ Aliases รับรองว่าเพื่อนๆจะสามารถใช้ GraphQL ได้อย่างปลอดภัยและมั่นใจแน่นอน ถ้ามีโอกาส อย่าลืมกลับมาดูแลระบบของตัวเองด้วยนะครับ เพราะการป้องกันไว้ก่อนย่อมดีกว่าการตามแก้ไขทีหลัง แล้วพบกันใหม่ในบทความหน้า สวัสดีครับ!