Kodokon kodokon.com

ความปลอดภัย: การฉีด SQL สิทธิ์ และคำสั่งที่เตรียมไว้

จำลองการฉีด SQL ลบล้างมันด้วยคำสั่งที่เตรียมไว้ และใช้หลักสิทธิ์ขั้นต่ำ

11 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

การฉีด SQL (SQL injection) เกิดขึ้นในขณะที่ข้อมูลที่ไม่น่าเชื่อถือถูกนำไปต่อเข้ากับข้อความของการสืบค้น ผู้โจมตีป้อนค่าที่ปิดสายอักขระและยึดเอาตรรกะไปควบคุม มาจำลองการโจมตีบนตาราง users กัน: การนำอินพุต ada' OR '1'='1 มาต่อโดยตรงเปลี่ยนตัวกรองที่แม่นยำให้กลายเป็นเงื่อนไขที่เป็นจริงเสมอ

SQL
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' เพิ่มเงื่อนไขที่เป็นจริงเสมอ อีกรูปแบบหนึ่งจะต่อท้ายด้วย -- เพื่อคอมเมนต์ส่วนที่เหลือของการสืบค้นเดิมทิ้ง บทเรียนคือ: ตราบใดที่ข้อมูลและโค้ดใช้สายอักขระเดียวกัน ไม่มีการหลีกอักขระด้วยมือใดที่เชื่อถือได้อย่างแท้จริง

SQL
-- Positional placeholder
SELECT * FROM users WHERE name = ?;

-- Named placeholder
SELECT * FROM users WHERE name = :name;
พารามิเตอร์แบบผูกค่าสองรูปแบบใน SQLite

วิธีแก้คือ คำสั่งที่เตรียมไว้ (prepared statement): ไดรเวอร์จะคอมไพล์โครงสร้างของการสืบค้นก่อน จากนั้นจึง ผูก (bind) ค่าต่าง ๆ แยกต่างหาก ข้อมูลที่ถูกผูกจะไม่ถูกแจงเป็น SQL เลย มีเพียงการนำไปเปรียบเทียบเท่านั้น ของแถมคือ โครงสร้างที่คอมไพล์แล้วนำกลับมาใช้ซ้ำได้: การผูกค่าหลายค่าต่อเนื่องกันช่วยหลีกเลี่ยงการคอมไพล์แผนใหม่ในทุกครั้งที่เรียก

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
เพย์โหลดเดิม แต่ตอนนี้ไม่เป็นอันตราย

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. เหตุใดคำสั่งที่เตรียมไว้พร้อมพารามิเตอร์จึงป้องกันการฉีด SQL ได้?
    • เพราะมันเข้ารหัสค่าที่ส่งไป
    • เพราะค่าที่ถูกผูกถูกปฏิบัติเป็นข้อมูล ไม่เคยถูกแจงเป็น SQL
    • เพราะมันหลีกเครื่องหมายคำพูดในสายอักขระโดยอัตโนมัติ
    • เพราะมันจำกัดความยาวของอินพุตผู้ใช้
  2. เพย์โหลด ' OR '1'='1 ทำการฉีดสำเร็จเพราะมัน...
    • ทำให้หน่วยความจำของเซิร์ฟเวอร์ฐานข้อมูลเต็ม
    • ปิดค่าคงที่แบบสายอักขระ จากนั้นเพิ่มเงื่อนไขที่เป็นจริงเสมอ
    • ลบตารางที่การสืบค้นพุ่งเป้าไป
    • เปลี่ยนสิทธิ์ของผู้ใช้ปัจจุบัน
  3. คุณสามารถผูกชื่อตารางผ่านพารามิเตอร์ ? ได้หรือไม่?
    • ได้ ตัวยึดตำแหน่งรับตัวระบุได้ทุกชนิด
    • ไม่ได้: ตัวยึดตำแหน่งผูกได้เฉพาะค่า ตัวระบุต้องถูกตรวจสอบกับบัญชีขาว
    • ได้ แต่เฉพาะกับรูปแบบที่มีชื่ออย่าง :name เท่านั้น
    • ได้ ถ้าตารางนั้นมีอยู่แล้ว