ข้ามไปยังเนื้อหาหลัก
คู่มือระบบ เนื้อหาผ่านการตรวจสอบแล้ว

การใช้ Prepared Statement เพื่อป้องกัน SQL Injection

บทที่แล้วกล่าวถึงการใช้ `DELETE` statement ใน SQL และความสำคัญของ `WHERE` clause บทนี้แนะนำภัยคุกคาม SQL Injection ที่แฮกเกอร์สามารถใช้แทรกโค้ด SQL อันตรายได้ เ...

การใช้ Prepared Statement เพื่อป้องกัน SQL Injection

ในบทเรียนที่แล้ว เราได้เรียนรู้วิธีการลบข้อมูลออกจากฐานข้อมูล MySQL ด้วยคำสั่ง DELETE ผ่าน PHP และเน้นย้ำถึงความสำคัญของการระบุเงื่อนไข (WHERE clause) เพื่อป้องกันการลบข้อมูลที่ไม่ต้องการ อย่างไรก็ตาม ยังมีภัยคุกคามที่สำคัญอีกอย่างที่เราต้องระวัง นั่นคือ SQL Injection ซึ่งเป็นช่องโหว่ที่แฮกเกอร์สามารถใช้เพื่อแทรกโค้ด SQL ที่เป็นอันตรายเข้าไปใน query ของเรา ทำให้พวกเขาสามารถเข้าถึง, แก้ไข, หรือลบข้อมูลในฐานข้อมูลของเราได้ตามต้องการ

ในบทนี้ เราจะมาเรียนรู้วิธีการป้องกัน SQL Injection โดยใช้ Prepared Statement ซึ่งเป็นเทคนิคที่มีประสิทธิภาพและใช้งานง่ายในการสร้าง API และ Web Services ที่ปลอดภัย

SQL Injection คืออะไร?

SQL Injection เกิดขึ้นเมื่อข้อมูลที่ผู้ใช้ป้อนเข้าไปในแบบฟอร์มหรือ URL ถูกนำไปใช้สร้าง SQL query โดยตรง โดยไม่มีการตรวจสอบหรือกรองข้อมูลอย่างเหมาะสม หากแฮกเกอร์สามารถใส่โค้ด SQL ที่เป็นอันตรายเข้าไปในข้อมูลที่ป้อนได้ พวกเขาก็สามารถควบคุมการทำงานของ query และเข้าถึงข้อมูลในฐานข้อมูลได้

ตัวอย่าง: สมมติว่าเรามีโค้ด PHP ที่ใช้สร้าง query เพื่อดึงข้อมูลผู้ใช้จากฐานข้อมูลดังนี้:

$username = $_GET['username']; // รับค่า username จาก URL
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = $conn->query($sql);

หากผู้ใช้ป้อน ' OR '1'='1 เป็นค่า username query ที่ถูกสร้างขึ้นจะเป็น:

SELECT * FROM users WHERE username = '' OR '1'='1'

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

Prepared Statement คืออะไร?

Prepared Statement เป็นเทคนิคในการสร้าง SQL query ที่แบ่งออกเป็นสองขั้นตอน:

  1. Preparation: สร้างโครงสร้างของ query โดยใช้ placeholder (เช่น ? หรือ :named_placeholder) แทนที่ค่าที่จะถูกใส่ลงไปใน query
  2. Execution: ส่งค่าที่จะถูกใส่ลงไปใน query ไปยังฐานข้อมูลแยกต่างหากจากโครงสร้างของ query

การทำเช่นนี้ทำให้ฐานข้อมูลสามารถแยกแยะระหว่างโค้ด SQL กับข้อมูลที่ผู้ใช้ป้อนได้ ทำให้ไม่สามารถแทรกโค้ด SQL ที่เป็นอันตรายเข้าไปใน query ได้

การใช้งาน Prepared Statement ใน PHP

ในการใช้งาน Prepared Statement ใน PHP เราจะใช้ PDO (PHP Data Objects) ซึ่งเป็น extension ที่มีอยู่ใน PHP โดยทั่วไป

ขั้นตอนการใช้งาน:

  1. สร้างการเชื่อมต่อกับฐานข้อมูล:
  2. try {
        $conn = new PDO("mysql:host=localhost;dbname=mydatabase", "username", "password");
        // set the PDO error mode to exception
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        echo "Connected successfully";
        } catch(PDOException $e) {
        echo "Connection failed: " . $e->getMessage();
        }
      
  3. เตรียม Prepared Statement:
  4. $stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
      

    ในตัวอย่างนี้ ? คือ placeholder ที่จะถูกแทนที่ด้วยค่า username ในขั้นตอนต่อไป

  5. Bind ค่าให้กับ Placeholder:
  6. $username = $_GET['username'];
    $stmt->bindParam(1, $username); // 1 คือลำดับของ placeholder (เริ่มจาก 1)
      

    เราใช้ bindParam() เพื่อผูกค่า $username กับ placeholder ตัวแรกใน query

  7. Execute Query:
  8. $stmt->execute();
      

    หลังจาก bind ค่าแล้ว เราก็สามารถ execute query ได้

  9. ดึงข้อมูล:
  10. $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
      

    เราใช้ fetchAll() เพื่อดึงข้อมูลทั้งหมดที่ได้จาก query ในรูปแบบ associative array

ตัวอย่างโค้ดทั้งหมด:

<?php
try {
    $conn = new PDO("mysql:host=localhost;dbname=mydatabase", "username", "password");
    // set the PDO error mode to exception
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $username = $_GET['username'];

    $stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
    $stmt->bindParam(1, $username);
    $stmt->execute();

    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);

    foreach($result as $row) {
        echo "Username: " . $row["username"] . "<br>";
        echo "Email: " . $row["email"] . "<br>";
    }

    } catch(PDOException $e) {
    echo "Error: " . $e->getMessage();
    }
$conn = null;
?>

ข้อดีของการใช้ Prepared Statement

  • ป้องกัน SQL Injection: นี่คือข้อดีที่สำคัญที่สุด Prepared Statement ช่วยให้เรามั่นใจได้ว่าข้อมูลที่ผู้ใช้ป้อนจะไม่ถูกนำไปใช้เพื่อแทรกโค้ด SQL ที่เป็นอันตราย
  • เพิ่มประสิทธิภาพ: เมื่อเราใช้ Prepared Statement หลายครั้งกับ query ที่มีโครงสร้างเหมือนกัน ฐานข้อมูลจะสามารถ compile query นั้นได้เพียงครั้งเดียว และใช้ซ้ำได้ในภายหลัง ทำให้การ execute query เร็วกว่าการสร้าง query ใหม่ทุกครั้ง
  • อ่านง่ายและบำรุงรักษาง่าย: โค้ดที่ใช้ Prepared Statement มักจะอ่านง่ายกว่าและบำรุงรักษาง่ายกว่า เพราะโครงสร้างของ query จะถูกแยกออกจากข้อมูลที่ผู้ใช้ป้อน

กรณีใช้งานจริงและข้อผิดพลาดที่พบบ่อย

กรณีใช้งานจริง:

  • การสร้าง API ที่รับข้อมูลจากผู้ใช้ผ่าน URL หรือ form
  • การสร้างระบบ login ที่ต้องตรวจสอบ username และ password
  • การสร้างระบบจัดการข้อมูลที่ต้องเพิ่ม, แก้ไข, หรือลบข้อมูลในฐานข้อมูล

ข้อผิดพลาดที่พบบ่อย:

  • ลืมใช้ Prepared Statement: นี่คือข้อผิดพลาดที่ร้ายแรงที่สุด หากเราไม่ใช้ Prepared Statement เราจะเปิดโอกาสให้แฮกเกอร์สามารถโจมตีระบบของเราได้
  • ใช้ string interpolation แทนการ bind ค่า: การใช้ string interpolation (เช่น การใส่ค่าลงใน string โดยตรง) จะทำให้เราไม่สามารถป้องกัน SQL Injection ได้
  • bind ค่าผิดประเภท: การ bind ค่าผิดประเภทอาจทำให้ query ทำงานไม่ถูกต้อง หรือทำให้เกิด error
  • ไม่จัดการกับ error: หากเกิด error ในระหว่างการ execute query เราควรจัดการกับ error อย่างเหมาะสม เพื่อป้องกันไม่ให้ข้อมูลรั่วไหล

สรุป

Prepared Statement เป็นเครื่องมือที่สำคัญในการป้องกัน SQL Injection ซึ่งเป็นภัยคุกคามที่ร้ายแรงต่อ API และ Web Services ของเรา การใช้งาน Prepared Statement อาจดูยุ่งยากในตอนแรก แต่เมื่อเราเข้าใจหลักการและขั้นตอนการใช้งานแล้ว มันจะกลายเป็นส่วนหนึ่งของ workflow การพัฒนา API ที่ปลอดภัยของเรา

ในบทเรียนถัดไป เราจะมาเรียนรู้วิธีการสร้าง API Key สำหรับ Authentication เพื่อป้องกันไม่ให้ผู้ที่ไม่ได้รับอนุญาตเข้าถึง API ของเรา

Office.in.th Custom Service

ต้องการพัฒนาระบบหรือปรับแต่งจากโครงสร้างนี้?

Office.in.th รับพัฒนาและออกแบบ Enterprise Web Application, API Integration และระบบบริหารงานภาครัฐ/เอกชน พร้อมการสนับสนุนระยะยาว

ดูบริการรับพัฒนาซอฟต์แวร์