Stored Procedure มีความสามารถในการจัดการกับข้อมูลในหลายตารางได้ในเวลาเดียวกัน
นั่นหมายความว่าถ้าใช้งานไม่ระวัง ก็อาจทำให้เกิดปัญหาด้านความปลอดภัย, การควบคุมสิทธิ์, หรือข้อมูลรั่วไหลได้
MySQL จึงมีเครื่องมือที่ช่วยให้เราสามารถควบคุมการเข้าถึงและการทำงานของ Procedure ได้อย่างเหมาะสม เช่น
SQL SECURITYGRANT EXECUTE- การแยกระดับผู้ใช้
- การป้องกัน SQL Injection
SQL SECURITY ควบคุมว่าคำสั่งจะรันด้วยสิทธิ์ของใคร
เมื่อสร้าง Stored Procedure เราสามารถกำหนดได้ว่า MySQL ควรใช้สิทธิ์ของใครในการรันคำสั่งภายในนั้น
CREATE DEFINER = 'admin'@'localhost'
PROCEDURE my_proc()
SQL SECURITY [DEFINER|INVOKER]
BEGIN
...
END;DEFINERหมายความว่า คำสั่งภายใน Procedure จะรันด้วยสิทธิ์ของผู้สร้าง (เจ้าของ Procedure)INVOKERหมายความว่า คำสั่งจะรันด้วยสิทธิ์ของผู้ที่เรียกใช้ Procedure นั้น ๆ- ค่า Default คือ
DEFINER
ตัวอย่าง: ให้คนเรียกใช้ดูเฉพาะข้อมูลตัวเองเท่านั้น
CREATE DEFINER = 'admin'@'localhost'
PROCEDURE view_my_orders(IN user_id INT)
SQL SECURITY INVOKER
BEGIN
SELECT * FROM orders WHERE customer_id = user_id;
END;- แม้ Procedure จะเข้าถึงตาราง
ordersได้ แต่หากผู้ใช้คนนั้นไม่มีสิทธิ์เข้าถึงordersโดยตรง ก็จะถูกบล็อกโดยอัตโนมัติ
GRANT EXECUTE ควบคุมว่าใครเรียก Procedure ได้บ้าง
เราสามารถกำหนดสิทธิ์การเรียกใช้งาน Stored Procedure ด้วยคำสั่ง GRANT
GRANT EXECUTE ON PROCEDURE db_name.proc_name TO 'username'@'host';
ตัวอย่าง
GRANT EXECUTE ON PROCEDURE mydb.process_order TO 'staff'@'%';- หากไม่ได้ให้สิทธิ์ EXECUTE ผู้ใช้จะไม่สามารถเรียก
CALL process_order()ได้เลย
การแยกระดับผู้ใช้งาน (User Roles)
ควรแยก User สำหรับดูข้อมูล (Read-only) และจัดการข้อมูล (Admin) ออกจากกัน หากใช้ Procedure ที่มีคำสั่งสำคัญ เช่น DELETE, UPDATE ควรกำหนดสิทธิ์ในการเรียกให้เฉพาะบาง user เท่านั้น
| Role | สิทธิ์ทั่วไป |
|---|---|
| viewer | SELECT, EXECUTE only |
| operator | SELECT, INSERT, UPDATE |
| admin | ALL PRIVILEGES |
ป้องกัน SQL Injection ภายใน Procedure
MySQL Stored Procedure ไม่ปลอดภัยจาก SQL Injection หากมีการใช้ Dynamic SQL (PREPARE, EXECUTE)
ตัวอย่างที่เสี่ยง
SET @query = CONCAT('SELECT * FROM ', table_name);
PREPARE stmt FROM @query;
EXECUTE stmt;- หากตัวแปร
table_nameมาจาก user โดยตรง และไม่ผ่านการกรอง จะเสี่ยงต่อการโดนแฮกข้อมูล หรือรันคำสั่งอันตรายได้
แนวทางป้องกัน
- หลีกเลี่ยงการใช้ Dynamic SQL หากไม่จำเป็น
- ถ้าต้องใช้ ตรวจสอบ whitelist ของ input อย่างเข้มงวด
ตรวจสอบสิทธิ์ของผู้เรียกใช้ด้วย USER()
เราสามารถเขียน Logic ภายใน Procedure ให้ตรวจสอบว่าใครเป็นคนเรียก แล้วจำกัดสิทธิ์ได้ด้วย
IF USER() NOT LIKE 'admin@%' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'คุณไม่มีสิทธิ์ใช้งาน Procedure นี้';
END IF;ข้อควรระวัง
- หลีกเลี่ยงการใช้
GRANT ALL ON *.*แบบกว้างขวางเกินไป - อย่าฝังพาสเวิร์ด หรือข้อมูลสำคัญไว้ใน Procedure โดยตรง
- หาก Procedure มีการอัปเดตข้อมูล ต้องตรวจสอบเงื่อนไขให้รัดกุมเสมอ