จำลองการฉีด SQL ลบล้างมันด้วยคำสั่งที่เตรียมไว้ และใช้หลักสิทธิ์ขั้นต่ำ
เปิดบทเรียนนี้ใน Kodokonการฉีด SQL (SQL injection) เกิดขึ้นในขณะที่ข้อมูลที่ไม่น่าเชื่อถือถูกนำไปต่อเข้ากับข้อความของการสืบค้น ผู้โจมตีป้อนค่าที่ปิดสายอักขระและยึดเอาตรรกะไปควบคุม มาจำลองการโจมตีบนตาราง users กัน: การนำอินพุต ada' OR '1'='1 มาต่อโดยตรงเปลี่ยนตัวกรองที่แม่นยำให้กลายเป็นเงื่อนไขที่เป็นจริงเสมอ
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
is_admin INTEGER NOT NULL DEFAULT 0
);
INSERT INTO users (id, name, is_admin)
VALUES (1, 'ada', 0), (2, 'root', 1);
-- Dangerous concatenation (NEVER do this)
-- input = ada' OR '1'='1
SELECT * FROM users
WHERE name = 'ada' OR '1'='1';เพย์โหลดนี้ทำงานได้เพราะเครื่องหมายคำพูดปิดค่าคงที่ 'ada' ก่อนเวลาอันควร จากนั้น OR '1'='1' เพิ่มเงื่อนไขที่เป็นจริงเสมอ อีกรูปแบบหนึ่งจะต่อท้ายด้วย -- เพื่อคอมเมนต์ส่วนที่เหลือของการสืบค้นเดิมทิ้ง บทเรียนคือ: ตราบใดที่ข้อมูลและโค้ดใช้สายอักขระเดียวกัน ไม่มีการหลีกอักขระด้วยมือใดที่เชื่อถือได้อย่างแท้จริง
-- Positional placeholder
SELECT * FROM users WHERE name = ?;
-- Named placeholder
SELECT * FROM users WHERE name = :name;วิธีแก้คือ คำสั่งที่เตรียมไว้ (prepared statement): ไดรเวอร์จะคอมไพล์โครงสร้างของการสืบค้นก่อน จากนั้นจึง ผูก (bind) ค่าต่าง ๆ แยกต่างหาก ข้อมูลที่ถูกผูกจะไม่ถูกแจงเป็น SQL เลย มีเพียงการนำไปเปรียบเทียบเท่านั้น ของแถมคือ โครงสร้างที่คอมไพล์แล้วนำกลับมาใช้ซ้ำได้: การผูกค่าหลายค่าต่อเนื่องกันช่วยหลีกเลี่ยงการคอมไพล์แผนใหม่ในทุกครั้งที่เรียก
-- The bound value stays data, not SQL
SELECT * FROM users
WHERE name = :name;
-- :name = ada' OR '1'='1
-- -> 0 rows: no user
-- is literally named that' OR '1'='1 ทำการฉีดสำเร็จเพราะมัน...? ได้หรือไม่?:name เท่านั้น