メインコンテンツへ移動

เนื้อหาภาษาไทย / タイ語のコンテンツ

システムガイド 内容確認済み

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

บทความนี้อธิบายถึงภัยคุกคาม SQL Injection ที่อาจเกิดขึ้นเมื่อทำงานกับฐานข้อมูล SQLite และแนะนำวิธีการป้องกันโดยใช้ Prepared Statements (หรือ Parameterized Queri...

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

ในบทความก่อนหน้า เราได้เรียนรู้วิธีการใช้งาน SQLite ร่วมกับ PHP เพื่อจัดการข้อมูลในฐานข้อมูล ไม่ว่าจะเป็นการเพิ่ม, แก้ไข, ลบ, หรืออ่านข้อมูล แต่สิ่งหนึ่งที่สำคัญอย่างยิ่งในการพัฒนาแอปพลิเคชันที่ทำงานกับฐานข้อมูลคือเรื่องของความปลอดภัย โดยเฉพาะอย่างยิ่งการป้องกัน SQL Injection ในบทนี้ เราจะมาเรียนรู้วิธีการใช้ Prepared Statements เพื่อป้องกันภัยคุกคามนี้ใน SQLite

SQL Injection คืออะไร?

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

ตัวอย่าง:

สมมติว่าเรามีฟอร์มล็อกอินที่รับชื่อผู้ใช้และรหัสผ่าน และสร้างคำสั่ง SQL เพื่อตรวจสอบข้อมูลดังนี้:

$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";

// Execute the query (อันตราย!)

หากผู้ใช้งานใส่ข้อมูลต่อไปนี้ในช่องชื่อผู้ใช้:

' OR '1'='1

คำสั่ง SQL ที่ถูกสร้างขึ้นจะเป็นดังนี้:

SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'

เนื่องจากเงื่อนไข '1'='1' เป็นจริงเสมอ คำสั่งนี้จะดึงข้อมูลผู้ใช้ทั้งหมดในตาราง users ออกมา ทำให้ผู้โจมตีสามารถล็อกอินเข้าสู่ระบบได้โดยไม่ต้องรู้รหัสผ่านที่ถูกต้อง นี่คือตัวอย่างง่ายๆ ของ SQL Injection ซึ่งอาจนำไปสู่ความเสียหายที่ร้ายแรงกว่านี้ได้

Prepared Statements คืออะไร?

Prepared Statements หรือ Parameterized Queries เป็นวิธีการสร้างคำสั่ง SQL ที่แยกส่วนของคำสั่ง (โครงสร้าง SQL) ออกจากข้อมูลที่จะใช้ในคำสั่ง (พารามิเตอร์) โดยโครงสร้าง SQL จะถูกเตรียมไว้ล่วงหน้า (pre-compiled) และพารามิเตอร์จะถูกส่งเข้าไปในคำสั่ง SQL ที่เตรียมไว้ในภายหลัง

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

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

วิธีการใช้งาน Prepared Statements ใน SQLite ด้วย PHP

ในการใช้งาน Prepared Statements ใน SQLite ด้วย PHP เราจะใช้ PDO (PHP Data Objects) ซึ่งเป็น extension ที่ช่วยให้เราสามารถเข้าถึงฐานข้อมูลต่างๆ ได้อย่างง่ายดายและปลอดภัย

  1. เชื่อมต่อกับฐานข้อมูล SQLite ด้วย PDO:
  2. try {
        $db = new PDO('sqlite:mydatabase.db');
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Enable error reporting
    } catch (PDOException $e) {
        echo "Connection failed: " . $e->getMessage();
        exit;
    }
        

    คำอธิบาย:

    • new PDO('sqlite:mydatabase.db'): สร้าง object PDO เพื่อเชื่อมต่อกับฐานข้อมูล SQLite ชื่อ mydatabase.db
    • $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION): ตั้งค่าให้ PDO แสดง error ในรูปแบบ exception ซึ่งช่วยให้เราจัดการ error ได้ง่ายขึ้น
  3. เตรียมคำสั่ง SQL ด้วย Prepared Statement:
  4. $stmt = $db->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
        

    คำอธิบาย:

    • $db->prepare("..."): เตรียมคำสั่ง SQL โดยใช้ placeholder :username และ :password แทนที่ค่าที่จะใส่ในภายหลัง
  5. ผูกค่า (bind) พารามิเตอร์:
  6. $username = $_POST['username'];
    $password = $_POST['password'];
    
    $stmt->bindParam(':username', $username);
    $stmt->bindParam(':password', $password);
        

    คำอธิบาย:

    • $stmt->bindParam(':username', $username): ผูกค่าของตัวแปร $username กับ placeholder :username
    • $stmt->bindParam(':password', $password): ผูกค่าของตัวแปร $password กับ placeholder :password

    ข้อควรระวัง: การใช้ bindParam จะส่งค่าของตัวแปรโดย reference ซึ่งหมายความว่าถ้าค่าของตัวแปรเปลี่ยนไปหลังจาก bind แล้ว ค่าที่ใช้ใน query ก็จะเปลี่ยนตามไปด้วย ถ้าต้องการส่งค่าโดย value สามารถใช้ bindValue แทนได้

  7. Execute คำสั่ง SQL:
  8. $stmt->execute();
        

    คำอธิบาย:

    • $stmt->execute(): Execute คำสั่ง SQL ที่เตรียมไว้
  9. ดึงข้อมูล:
  10. $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    if (count($result) > 0) {
        // Login successful
        echo "Login successful!";
    } else {
        // Login failed
        echo "Login failed!";
    }
        

    คำอธิบาย:

    • $stmt->fetchAll(PDO::FETCH_ASSOC): ดึงข้อมูลทั้งหมดที่ได้จากการ query ในรูปแบบ associative array

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

<?php

try {
    $db = new PDO('sqlite:mydatabase.db');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
    exit;
}

$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $db->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();

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

if (count($result) > 0) {
    echo "Login successful!";
} else {
    echo "Login failed!";
}

?>

ด้วยการใช้ Prepared Statements เราสามารถป้องกัน SQL Injection ได้อย่างมีประสิทธิภาพ และทำให้แอปพลิเคชันของเราปลอดภัยมากยิ่งขึ้น

Trade-offs: การใช้ Prepared Statements อาจทำให้โค้ดดูยาวขึ้นเล็กน้อย แต่สิ่งที่ได้กลับมาคือความปลอดภัยที่มากขึ้น ซึ่งคุ้มค่าอย่างยิ่ง

แนวปฏิบัติที่ดี:

  • ใช้ Prepared Statements เสมอเมื่อทำงานกับข้อมูลที่มาจากผู้ใช้งาน
  • หลีกเลี่ยงการสร้างคำสั่ง SQL โดยตรงจากการต่อ string
  • ตรวจสอบและ validate ข้อมูลที่ได้รับจากผู้ใช้งานก่อนนำไปใช้งาน

ในบทความนี้ เราได้เรียนรู้วิธีการป้องกัน SQL Injection โดยใช้ Prepared Statements ใน SQLite ซึ่งเป็นสิ่งสำคัญอย่างยิ่งในการพัฒนาแอปพลิเคชันที่ปลอดภัย ในบทต่อไป เราจะสรุปเนื้อหาทั้งหมดที่เราได้เรียนรู้ในบทเรียน SQLite ฉบับเริ่มต้นนี้

Office.in.th Custom Service

この構成をもとにシステムを開発・カスタマイズしたいですか?

Office.in.th は、エンタープライズ Web アプリケーション、API 連携、官民の業務管理システムの設計・開発を承り、長期的なサポートも提供します。

ソフトウェア開発サービスを見る