Sql Injection Part 1

Portswigger AcademyPart 1
b
benzdeus
Mar 15, 2026·2 min read

เกริ่นนำ

โพสต์นี้พาแก้ Lab แรกของ PortSwigger Academy ในหัวข้อ SQL Injection vulnerability in WHERE clause allowing retrieval of hidden data
โจทย์ข้อนี้เป็นตัวอย่างพื้นฐานที่ดีมากสำหรับการเริ่มต้นทำความเข้าใจ SQL Injection เพราะจุดอ่อนอยู่ในพารามิเตอร์ category ที่ถูกนำไปประกอบเป็นคำสั่ง SQL โดยตรง

ภาพรวมของโจทย์คือ แอปพลิเคชันจะดึงข้อมูลสินค้าด้วย query ลักษณะนี้

SQL
SELECT * FROM products WHERE category = 'Gifts' AND released = 1

ความหมายของ query นี้คือ:

  • เลือกสินค้าจากหมวด Gifts

  • และแสดงเฉพาะสินค้าที่ถูกปล่อยแล้ว (released = 1)

เป้าหมายของเราคือทำให้ระบบแสดง สินค้าที่ซ่อนอยู่หรือยังไม่ถูกปล่อย ออกมาด้วย

ภาพหน้าโจทย์

วิเคราะห์ช่องทางรับค่า

เมื่อเปิดหน้า Lab ขึ้นมา เราจะเห็นว่าเว็บมีการกรองสินค้าตามหมวดหมู่ และเมื่อคลิกหมวดใดหมวดหนึ่ง ค่า category จะถูกส่งไปใน URL

ตัวอย่างเช่น เมื่อเลือกหมวด Clothing, shoes and accessories จะเห็นว่าหน้าเว็บเรียกข้อมูลผ่านพารามิเตอร์ category ชัดเจน

ภาพหน้าเว็บและพารามิเตอร์ category

จุดนี้เป็นสัญญาณสำคัญว่า input จากผู้ใช้มีโอกาสถูกนำไปต่อเป็น SQL statement ทางฝั่ง backend โดยตรง
และเมื่อชื่อ Lab ระบุชัดว่าเป็น SQL Injection เราจึงเริ่มลองทดสอบด้วย payload พื้นฐานได้เลย


ทำความเข้าใจกับ payload ที่ใช้

แนวคิดของ SQL Injection ข้อนี้คือ “ปิด string เดิม แล้วเติมเงื่อนไขของเราเข้าไป” จากนั้นใช้ comment เพื่อตัดส่วนที่เหลือของ query ทิ้ง

payload ที่ใช้คือ

SQL
' OR '1'='1' --

มาดูทีละส่วน:

1) '

single quote ใช้สำหรับปิด string เดิมที่ระบบกำลังสร้างอยู่

ตัวอย่าง query ปกติ:

SQL
SELECT * FROM users WHERE firstname = 'สมชาย'

ถ้าเราควบคุมค่าภายใน string นี้ได้ เราก็อาจปิด string ก่อนเวลาอันควร แล้วต่อ logic ของเราเพิ่มเข้าไปได้

2) OR '1'='1'

นี่คือเงื่อนไขที่เป็นจริงเสมอ (true)
เพราะ '1'='1' ให้ผลเป็นจริงตลอด

3) --

ใช้สำหรับ comment ส่วนที่เหลือของ query ออกไป
ทำให้เงื่อนไขหลังจาก payload ของเราไม่ถูกประมวลผลต่อ


Query ถูกเปลี่ยนเป็นอะไร

เดิมระบบน่าจะสร้าง query ประมาณนี้

SQL
SELECT * FROM products WHERE category = 'Gifts' AND released = 1

ถ้าเราฉีด payload เข้าไปใน category เช่น

text
Gifts' OR '1'='1' --

query จะกลายเป็น

SQL
SELECT * FROM products WHERE category = 'Gifts' OR '1'='1' -- ' AND released = 1

สิ่งที่เกิดขึ้นคือ:

  • เงื่อนไข category = 'Gifts' ยังอยู่

  • แต่มี OR '1'='1' เพิ่มเข้ามา ทำให้เงื่อนไขรวมเป็นจริง

  • ส่วน AND released = 1 ถูก comment ทิ้ง

ผลลัพธ์คือระบบจะไม่จำกัดเฉพาะสินค้าที่ถูกปล่อยแล้วอีกต่อไป
ดังนั้นสินค้าที่ซ่อนอยู่หรือ unreleased products ก็ถูกดึงออกมาแสดงได้ด้วย


ทำไมถึง bypass ได้

หัวใจของข้อนี้อยู่ที่ตรรกะของเงื่อนไข

เดิม:

SQL
category = 'Gifts' AND released = 1

หลังฉีด:

SQL
category = 'Gifts' OR '1'='1'

เพราะ OR '1'='1' เป็นจริงเสมอ
เงื่อนไขทั้งหมดจึงมีแนวโน้มให้ผลเป็นจริงสำหรับทุกแถว ส่งผลให้ฐานข้อมูลคืนข้อมูลสินค้าทั้งหมดกลับมา

พูดง่าย ๆ คือเราเปลี่ยนจาก query ที่ “กรองข้อมูล” ให้กลายเป็น query ที่ “เปิดกว้าง” จนดึงข้อมูลเกินกว่าที่ระบบตั้งใจไว้


Payload ที่ใช้กับหน้า Lab

ตัวอย่าง payload ใน URL:

text
https://<lab-id>.web-security-academy.net/filter?category=Clothing,+shoes+and+accessories' OR '1'='1' --

เมื่อส่งค่าแบบนี้ไป หน้าเว็บจะแสดงสินค้าที่เดิมไม่ควรถูกมองเห็น และถือว่าแก้ Lab สำเร็จ

ภาพหลังฉีด payload แล้วแก้ Lab สำเร็จ

สรุปแนวคิดของโจทย์ข้อนี้

Lab นี้เป็นตัวอย่างคลาสสิกของ SQL Injection ใน WHERE clause โดยมีขั้นตอนคิดหลัก ๆ ดังนี้

  1. สังเกตว่าหน้าเว็บรับค่า category จาก URL

  2. เข้าใจว่าค่านี้น่าจะถูกนำไปประกอบ SQL query

  3. ใช้ single quote เพื่อปิด string เดิม

  4. เติมเงื่อนไขที่เป็นจริงเสมอด้วย OR '1'='1'

  5. ใช้ -- เพื่อ comment ส่วนที่เหลือของ query

  6. ทำให้ระบบดึงข้อมูลที่ไม่ควรแสดงออกมาได้


ข้อควรรู้เพิ่มเติม

แม้ข้อนี้จะเป็น Lab พื้นฐาน แต่แนวคิดเดียวกันสามารถต่อยอดไปสู่โจทย์ที่ซับซ้อนขึ้นได้ เช่น

  • การ bypass login

  • การดึงข้อมูลจากตารางอื่น

  • การใช้ UNION SELECT

  • การ enumerate schema หรือชื่อคอลัมน์

  • การ blind SQL injection

ดังนั้นถ้าเข้าใจโจทย์ข้อนี้ดี จะเป็นฐานสำคัญสำหรับ Lab ขั้นถัดไป


แนวทางป้องกัน

สาเหตุของปัญหาไม่ได้อยู่ที่เครื่องหมาย ' หรือ -- เพียงอย่างเดียว แต่เกิดจากการนำ input ของผู้ใช้ไปต่อกับ SQL โดยตรง

แนวทางป้องกันที่ถูกต้องคือ:

  • ใช้ parameterized queries / prepared statements

  • ตรวจสอบและจำกัดรูปแบบ input

  • หลีกเลี่ยงการประกอบ SQL ด้วย string ต่อกันตรง ๆ

  • ใช้หลัก least privilege สำหรับบัญชีฐานข้อมูล

ตัวอย่างเชิงแนวคิด:

SQL
SELECT * FROM products WHERE category = ? AND released = 1

การส่งค่าผ่าน parameter จะช่วยแยกระหว่าง “ข้อมูล” กับ “คำสั่ง” ทำให้ input ของผู้ใช้ไม่ถูกตีความเป็น SQL code


บทส่งท้าย

สำหรับ Lab แรกของ PortSwigger ข้อนี้ถือว่าเหมาะมากสำหรับการเริ่มต้น เพราะทำให้เห็นภาพชัดว่า
SQL Injection ไม่จำเป็นต้องซับซ้อนเสมอไป แค่ input ถูกนำไปต่อ query แบบไม่ปลอดภัย ก็อาจทำให้เงื่อนไขในระบบถูกบิดจนดึงข้อมูลที่ไม่ควรเห็นออกมาได้

ถ้าจะสรุปโจทย์นี้ให้สั้นที่สุด:

ปิด string เดิม เติมเงื่อนไขที่เป็นจริงเสมอ แล้ว comment ส่วนที่เหลือทิ้ง

แค่นี้ก็เพียงพอที่จะทำให้ Lab นี้ผ่านได้แล้ว

In This Series

View All Parts