การอนุญาตให้ User ส่งข้อมูลมาแก้ไข ( Update ) หรือสร้างใหม่ ( Create ) ในระบบ RESTful API เป็นจุดที่อันตรายที่สุดหากไม่มีการควบคุมที่ดีครับ เพราะ Hacker อาจส่ง Field ที่เราไม่ได้อนุญาต ( เช่น is_admin, wallet_balance ) เข้ามาใน Request Body เพื่อแอบแก้ไขข้อมูลสำคัญได้
นี่คือสิ่งที่ต้องระวังและจัดการเพื่อให้ API ของคุณปลอดภัยครับ
การควบคุมฟิลด์ที่อนุญาต ( Mass Assignment Protection )
นี่คือเรื่องที่สำคัญที่สุด หรือที่เรียกว่า “Allowlist” ( ไม่ใช่ Blocklist ) คือเราต้องระบุให้ชัดเจนว่า Column ไหนบ้างที่อนุญาตให้แก้ไขได้
- Allowed Columns ( Fillable ): กำหนดใน Model หรือ Controller ว่า field ไหนที่รับค่าได้ เช่น
first_name,last_name,phone - Guarded Columns: ห้ามรับค่าอย่างเด็ดขาด เช่น
id,role,password_hash,created_at
การทำ Data Validation & Sanitization
อย่าเชื่อใจข้อมูลที่มาจาก Client แม้แต่ตัวอักษรเดีย
- Type Checking: ถ้าช่องนี้เป็นตัวเลข ( Integer ) ต้องตรวจสอบว่าเป็นตัวเลขจริงไหม
- Range & Length: เบอร์โทรศัพท์ต้องไม่เกิน 10 หลัก, อายุต้องอยู่ระหว่าง 0-120
- Sanitization: ล้างอักขระพิเศษ (เช่น
<script>) เพื่อป้องกัน XSS (Cross-Site Scripting) และตรวจสอบ Single Quote เพื่อป้องกัน SQL Injection
การตรวจสอบสิทธิ์ ( Authorization )
ไม่ใช่แค่ Login ผ่าน (Authentication) แล้วจะแก้ได้ทุกอย่าง ต้องเช็คด้วยว่า “เขามีสิทธิ์แก้ Record นี้ไหม”
- Ownership Check: User A ต้องไม่สามารถใช้
PATCH /users/1เพื่อไปแก้ข้อมูลของ User B ได้ - Role-Based Access Control ( RBAC ): เฉพาะ Admin เท่านั้นที่เปลี่ยนสถานะ
status: "verified"ของบทความได้
ความแตกต่างระหว่าง PUT และ PATCH
การใช้ Method ให้ถูกช่วยลดโอกาสข้อมูลพัง ( Data Integrity ) ได้
- PUT: เป็นการ “แทนที่” ทั้งหมด หากส่งมาไม่ครบ Field ที่หายไปอาจกลายเป็น NULL ( อันตรายมาก )
- PATCH: เป็นการ “แก้ไขบางส่วน” เฉพาะ Field ที่ส่งมา ซึ่งปลอดภัยและนิยมมากกว่าในการทำ Update
การใช้ DTO ( Data Transfer Object )
ในระบบขนาดใหญ่ เราจะไม่ส่ง Request Object ตรงๆ เข้าไปที่ฐานข้อมูล แต่จะใช้ DTO เป็นตัวคั่น
- รับ Request เข้ามา
- Map เฉพาะข้อมูลที่ต้องการใส่ใน DTO ( ขั้นตอนนี้จะคัดฟิลด์ที่ไม่เกี่ยวออกไปโดยอัตโนมัติ )
- ส่ง DTO ไปจัดการต่อใน Service/Database
🛠 ตัวอย่างการจัดการในทางปฏิบัติ ( Pseudo-code )
// ตัวอย่างแบบอันตราย (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);
}
⚠️ ข้อควรระวังเพิ่มเติม ( Nginx & Security )
เมื่อคุณจัดการเรื่อง Security ในระดับ Code แล้ว อย่าลืมระดับ Infrastructure ด้วย
- Rate Limiting: ป้องกันการยิงถล่มเพื่อเดาข้อมูล ( Brute Force ) โดยตั้งค่าใน Nginx
- HTTPS Only: ข้อมูลที่ส่งผ่าน Method POST/PATCH ต้องถูกเข้ารหัสเสมอ เพื่อป้องกันการดักจับข้อมูลระหว่างทาง ( Man-in-the-Middle )
อ่านเพิ่มเติม
