หมวดหมู่: REST

RESTful: update dataRESTful: update data

การอนุญาตให้ User ส่งข้อมูลมาแก้ไข หรือสร้างใหม่ ในระบบ RESTful API เป็นจุดที่อันตรายที่สุดหากไม่มีการควบคุมที่ดีครับ เพราะ Hacker อาจส่ง Field ที่เราไม่ได้อนุญาต เข้ามาใน Request Body เพื่อแอบแก้ไขข้อมูลสำคัญได้

นี่คือสิ่งที่ต้องระวังและจัดการเพื่อให้ API ของคุณปลอดภัยครับ


การควบคุมฟิลด์ที่อนุญาต

นี่คือเรื่องที่สำคัญที่สุด หรือที่เรียกว่า “Allowlist” คือเราต้องระบุให้ชัดเจนว่า Column ไหนบ้างที่อนุญาตให้แก้ไขได้

  • Allowed Columns : กำหนดใน Model หรือ Controller ว่า field ไหนที่รับค่าได้ เช่น first_name, last_name, phone
  • Guarded Columns: ห้ามรับค่าอย่างเด็ดขาด เช่น id, role, password_hash, created_at

การทำ Data Validation & Sanitization

อย่าเชื่อใจข้อมูลที่มาจาก Client แม้แต่ตัวอักษรเดีย

  • Type Checking: ถ้าช่องนี้เป็นตัวเลข ต้องตรวจสอบว่าเป็นตัวเลขจริงไหม
  • Range & Length: เบอร์โทรศัพท์ต้องไม่เกิน 10 หลัก, อายุต้องอยู่ระหว่าง 0-120
  • Sanitization: ล้างอักขระพิเศษ (เช่น <script>) เพื่อป้องกัน XSS (Cross-Site Scripting) และตรวจสอบ Single Quote เพื่อป้องกัน SQL Injection

การตรวจสอบสิทธิ์

ไม่ใช่แค่ Login ผ่าน (Authentication) แล้วจะแก้ได้ทุกอย่าง ต้องเช็คด้วยว่า “เขามีสิทธิ์แก้ Record นี้ไหม”

  • Ownership Check: User A ต้องไม่สามารถใช้ PATCH /users/1 เพื่อไปแก้ข้อมูลของ User B ได้
  • Role-Based Access Control : เฉพาะ Admin เท่านั้นที่เปลี่ยนสถานะ status: "verified" ของบทความได้

ความแตกต่างระหว่าง PUT และ PATCH

การใช้ Method ให้ถูกช่วยลดโอกาสข้อมูลพัง ได้

  • PUT: เป็นการ “แทนที่” ทั้งหมด หากส่งมาไม่ครบ Field ที่หายไปอาจกลายเป็น NULL
  • PATCH: เป็นการ “แก้ไขบางส่วน” เฉพาะ Field ที่ส่งมา ซึ่งปลอดภัยและนิยมมากกว่าในการทำ Update

การใช้ DTO

ในระบบขนาดใหญ่ เราจะไม่ส่ง Request Object ตรงๆ เข้าไปที่ฐานข้อมูล แต่จะใช้ DTO เป็นตัวคั่น

  1. รับ Request เข้ามา
  2. Map เฉพาะข้อมูลที่ต้องการใส่ใน DTO
  3. ส่ง DTO ไปจัดการต่อใน Service/Database

🛠 ตัวอย่างการจัดการในทางปฏิบัติ

// ตัวอย่างแบบอันตราย (Don't do this) // รับมาทุกอย่างแล้วบันทึกเลย -> Hacker สามารถส่ง { "role": "admin" } มาได้
User.update (req.body) ; 

// ตัวอย่างแบบปลอดภัย (Allowed Columns) const allowedFields = ['first_name', 'last_name', 'bio'];
const updateData = {};

allowedFields.forEach (field => {
 if (req.body[field] !== undefined) {
 updateData[field] = req.body[field];
 }
}) ;

// ตรวจสอบสิทธิ์ก่อนบันทึก
if (user.id === req.user.id || req.user.isAdmin) {
 User.update (updateData) ;
}

⚠️ ข้อควรระวังเพิ่มเติม

เมื่อคุณจัดการเรื่อง Security ในระดับ Code แล้ว อย่าลืมระดับ Infrastructure ด้วย

  • Rate Limiting: ป้องกันการยิงถล่มเพื่อเดาข้อมูล โดยตั้งค่าใน Nginx
  • HTTPS Only: ข้อมูลที่ส่งผ่าน Method POST/PATCH ต้องถูกเข้ารหัสเสมอ เพื่อป้องกันการดักจับข้อมูลระหว่างทาง

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