การฉีด SQL
การแทรก SQL อยู่ในอันดับต้นๆ ของรายการช่องโหว่ด้านความปลอดภัยบนเว็บทุกรายการนับตั้งแต่ช่วงปลายทศวรรษ 1990 การแก้ไข — การสืบค้นแบบกำหนดพารามิเตอร์ — เป็นที่รู้จัก เรียบง่าย และได้รับการแนะนำมานานกว่าสองทศวรรษ ยังมีการค้นพบช่องโหว่ SQLi ใหม่ทุกสัปดาห์ หมวดหมู่ยังคงมีอยู่เนื่องจากโหมดความล้มเหลวเป็นแบบละเอียด และค่าเริ่มต้นของกรอบงานจำนวนมากไม่สมบูรณ์
เนื้อหาบทความฉบับเต็มมีให้เป็นภาษาอังกฤษด้านล่าง
SQL injection (SQLi) เป็นคลาสของช่องโหว่ที่ผู้โจมตีสามารถแทรกโค้ด SQL ลงในแบบสอบถามที่แอปพลิเคชันส่งไปยังฐานข้อมูล หากสำเร็จ ผู้โจมตีสามารถอ่าน แก้ไข หรือลบข้อมูลที่พวกเขาไม่ได้รับอนุญาตให้เข้าถึง บางครั้งรันโค้ดบนเซิร์ฟเวอร์ฐานข้อมูล และบางครั้งก็เปลี่ยนทิศทางเพื่อโจมตีแอปพลิเคชันนั้นเอง
ข้อผิดพลาดแบบคลาสสิก
ตัวอย่างหนังสือเรียน แอปพลิเคชันยอมรับชื่อผู้ใช้จากแบบฟอร์มเข้าสู่ระบบและค้นหา:
query = "SELECT * FROM users WHERE username='" + input + "';"If input is alice, แบบสอบถาม กลายเป็น:
SELECT * จากผู้ใช้ WHERE ชื่อผู้ใช้='alice';Normal แต่ถ้า input คือ alice' หรือ '1'='1 ข้อความค้นหาจะกลายเป็น:
SELECT * FROM users WHERE username='alice' OR '1'='1';The OR '1'='1' เป็นจริงเสมอ แบบสอบถามจะส่งกลับผู้ใช้ทุกคนในตาราง ด้วยเพย์โหลดที่ซับซ้อนมากขึ้น ผู้โจมตีสามารถถ่ายโอนข้อมูลตาราง แก้ไขบันทึก หรือขยายขนาดเพิ่มเติมได้
ประเภทการโจมตี
- Classic / in-band SQLi. ผลลัพธ์ของการสืบค้นที่แทรกไว้จะปรากฏในการตอบสนองของแอปพลิเคชัน ผู้โจมตีอ่านข้อมูลโดยตรง
- Blind SQLi. ไม่มีข้อมูลปรากฏในการตอบสนอง แต่พฤติกรรมของแอปพลิเคชันจะเปลี่ยนไปขึ้นอยู่กับว่าแบบสอบถามที่ฉีดจะส่งกลับค่าจริงหรือเท็จ ผู้โจมตีแยกข้อมูลทีละบิตโดยถามคำถามเช่น "เป็นอักขระตัวแรกของรหัสผ่านผู้ดูแลระบบ 'a' หรือไม่"
- Time-based blind SQLi. ผู้โจมตีใช้
SLEEP()หรือเทียบเท่าเพื่อทำให้ฐานข้อมูลตอบสนองช้าเมื่อเงื่อนไขเป็นจริง แม้กระทั่งการขโมยข้อมูลช้าลง แต่จะทำงานเมื่อแอปพลิเคชันไม่ให้การตอบสนอง - SQLi นอกแบนด์ ผู้โจมตีหลอกฐานข้อมูลให้สร้างการเชื่อมต่อเครือข่ายภายนอก (การค้นหา DNS, คำขอ HTTP) ที่ทำให้ข้อมูลรั่วไหลไปยังเซิร์ฟเวอร์ที่พวกเขาควบคุม
- SQL ลำดับที่สอง อินพุตที่เป็นอันตรายจะถูกเก็บไว้ในฐานข้อมูล ก่อน แล้วจึงอ่านและนำไปใช้ในแบบสอบถามอื่นอย่างไม่ปลอดภัยในภายหลัง การฉีดเกิดขึ้นในบริบทที่แตกต่างจากอินพุต
การแก้ไข: การสืบค้นแบบกำหนดพารามิเตอร์
วิธีที่ถูกต้องในการสืบค้นฐานข้อมูลด้วยการป้อนข้อมูลของผู้ใช้คือการใช้ตัวยึดตำแหน่งพารามิเตอร์ ไม่ต้องต่อสตริงเข้าด้วยกัน โดยพื้นฐานแล้วทุกภาษา:
// Python (psycopg2)
cursor.execute("เลือก * จากผู้ใช้ WHERE ชื่อผู้ใช้=%s", (อินพุต,))
//จาวา
stmt = conn.prepareStatement("SELECT * FROM users WHERE ชื่อผู้ใช้=?");
stmt.setString(1, อินพุต);
// จาวาสคริปต์ (pg)
client.query("SELECT * FROM users WHERE username=$1", [input]);ไดรเวอร์จะส่ง SQL และค่าพารามิเตอร์แยกกัน ฐานข้อมูลไม่เคยสับสนข้อมูลอินพุตกับไวยากรณ์ SQL แม้ว่าอินพุตจะมีเครื่องหมายคำพูด อัฒภาค หรือส่วนคำสั่ง OR หรือไวยากรณ์ SQL อื่นๆ ก็จะถือว่าเป็นข้อมูล
ใช้งานได้ เป็นแนวทางที่แนะนำมาเป็นเวลาสองทศวรรษแล้ว ความท้าทายไม่ใช่อยู่ที่เทคนิค ทำให้แน่ใจว่าทุกแบบสอบถามในโค้ดเบสใช้มัน
ORM จะไม่ปลอดภัยโดยอัตโนมัติ
Object-Relational Mappers (ActiveRecord, Sequelize, Hibernate, SQLAlchemy) ใช้แบบสอบถามแบบกำหนดพารามิเตอร์ตามค่าเริ่มต้น ซึ่งดีมาก แต่ ORM ทุกตัวมีช่อง Escape:
raw()วิธีการที่ใช้สตริง SQL ที่กำหนดเอง- String-based ORDER BY clauses (ชื่อคอลัมน์ไม่สามารถกำหนดพารามิเตอร์ได้)
- ชื่อตารางที่สอดแทรกเข้าไปในแบบสอบถาม
- กระบวนงานที่เก็บไว้นั้นเอง concatenate
Modern SQLi bugs มักจะปรากฏในกรณี Edge เหล่านี้ ไม่ใช่ในพาธโค้ด CRUD หลัก
NoSQLject
รูปแบบนี้ไม่ได้มีลักษณะเฉพาะใน SQL MongoDB, Redis, ElasticSearch, GraphQL — ภาษาคิวรีใดๆ ที่ยอมรับอินพุตที่มีโครงสร้างจากผู้ใช้สามารถฉีดเข้าไปได้ หากอินพุตไม่ได้รับการตรวจสอบความถูกต้อง ตัวดำเนินการ $ne, $gt, $regex ของ MongoDB เป็นเวกเตอร์การฉีดทั่วไปเมื่อเพย์โหลด JSON ไม่ได้ตรวจสอบสคีมาอย่างเคร่งครัด
Defense ในเชิงลึก
Beyond ที่กำหนดพารามิเตอร์ แบบสอบถาม:
- Least-privilege บัญชีฐานข้อมูล แอปพลิเคชันควรเชื่อมต่อกับผู้ใช้ที่มีสิทธิ์ที่จำเป็นเท่านั้น เส้นทางแบบอ่านอย่างเดียวใช้ข้อมูลรับรองแบบอ่านอย่างเดียว จำกัดความเสียหายเมื่อมีการใช้ประโยชน์จาก SQLi
- การตรวจสอบอินพุต จำกัดอินพุตให้อยู่ในรูปแบบที่คาดหวัง มีประโยชน์แต่ยังไม่เพียงพอในตัวเอง
- Web Application Firewalls (WAFs) รูปแบบตรงกับความพยายาม SQLi ที่ชัดเจน ผู้โจมตีสามารถข้ามผ่านได้แต่ทำให้ผู้โจมตีช้าลง
- การวิเคราะห์แบบคงที่ เครื่องมือที่สแกนแหล่งที่มาสำหรับการสืบค้นที่ต่อด้วยสตริง IDE สมัยใหม่ตั้งค่าสถานะสิ่งเหล่านี้
- Database hardening. ปิดการใช้งานคุณสมบัติที่ไม่จำเป็น (
xp_cmdshellใน MSSQL, ส่วนขยายที่เป็นอันตรายใน PostgreSQL)
Famous SQLi เหตุการณ์
- 2007 TJX — บัตรเครดิต 94 ล้านใบ เริ่มต้นด้วยการประนีประนอม Wi-Fi แต่ SQLi ถูกใช้เพื่อถอนข้อมูลจากฐานข้อมูลภายใน
- 2008 Heartland — บันทึกการ์ด 130 ล้านรายการ SQLi ให้สิทธิ์การเข้าถึงเครือข่ายครั้งแรก
- 2012 LinkedIn — แฮชรหัสผ่าน 117 ล้านแฮชที่ถูกกรองผ่าน SQLi
- 2017 Equifax — ช่องโหว่ Apache Struts แต่ SQLi เป็นส่วนหนึ่งของข้อมูลหลังการประนีประนอม การกรอง
- ตลอดปี 2020s — การละเมิดเล็กๆ น้อยๆ นับไม่ถ้วนที่บริษัท SaaS, ไซต์อีคอมเมิร์ซ, หน่วยงานภาครัฐ
หมวดหมู่นี้มีอายุหลายสิบปีและยังคงมีชีวิตอยู่อย่างมาก
คำถามที่พบบ่อย
- SQL injector ยังคงเป็นเรื่องปกติหรือไม่?
- ใช่. 10 อันดับแรกของ OWASP แสดงรายการการฉีด (ซึ่งรวมถึง SQLi) ในสามหมวดหมู่อันดับแรกทุกปี โปรแกรมรางวัลจุดบกพร่องจะเห็นรายงาน SQLi ทุกสัปดาห์ เฟรมเวิร์กสมัยใหม่ได้ลดความถี่ในพาธโค้ดหลัก แต่แอปรุ่นเก่า เคส Edge และ Escape Hatch ORM ทำให้หมวดหมู่นี้ยังคงอยู่
- Web Application Firewall สามารถหยุดการฉีด SQL ทั้งหมดได้หรือไม่
- ไม่ WAF จับรูปแบบที่ชัดเจนและหยุดผู้โจมตีที่ไม่ซับซ้อน แต่ผู้โจมตีที่มีความรู้สามารถสร้างการเลี่ยงผ่านได้ — การเข้ารหัสที่แตกต่างกัน การแทรกความคิดเห็น และไวยากรณ์ SQL ทางเลือก WAF เป็นเลเยอร์ที่มีประโยชน์ ไม่ได้ใช้แทนการสร้างแบบสอบถามที่ปลอดภัยในแอปพลิเคชัน
- HTTPS ป้องกันการฉีด SQL หรือไม่
- ไม่ SQLi เป็นช่องโหว่ในชั้นแอปพลิเคชัน HTTPS ปกป้องข้อมูลระหว่างการส่งแต่ไม่ได้ตรวจสอบเนื้อหา ข้อมูลที่เป็นอันตรายของผู้โจมตีมาถึงผ่าน HTTPS เช่นเดียวกับคนอื่นๆ แอปพลิเคชันคือสิ่งที่ดำเนินการหรือไม่ประมวลผลได้อย่างปลอดภัย
- ฐานข้อมูล NoSQL มีภูมิคุ้มกันหรือไม่
- ไม่ NoSQLjection เป็นหมวดหมู่ของตัวเอง MongoDB, Elasticsearch และอื่นๆ ยอมรับอินพุตคิวรีที่มีโครงสร้างซึ่งสามารถจัดการได้หากไม่ได้รับการตรวจสอบอย่างเข้มงวด รูปแบบการป้องกันจะคล้ายกัน (ถือว่าอินพุตของผู้ใช้เป็นข้อมูล ไม่ใช่เป็นโครงสร้างแบบสอบถาม) แต่การโจมตีเฉพาะจะแตกต่างกัน
- ฉันจะทดสอบไซต์ของตัวเองสำหรับการฉีด SQL ได้อย่างไร
- เครื่องมือเช่น sqlmap ทำการทดสอบอัตโนมัติสำหรับรูปแบบ SQLi ทั่วไป OWASP ZAP และ Burp Suite มีสแกนเนอร์ด้วย สำหรับนักพัฒนา การตรวจสอบที่น่าเชื่อถือที่สุดคือการตรวจสอบโค้ดและการวิเคราะห์แบบคงที่สำหรับการสืบค้นที่ต่อสตริง ภารกิจการทดสอบการเจาะระบบรวมถึงการประเมิน SQLi โดยเฉพาะ