Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ระบบเครือข่าย >> ความปลอดภัยเครือข่าย

PHP SQL Injection แกะลึกลับ: ปกป้องแอปพลิเคชันจากการโจมตี

ในหมวดหมู่ของการโจมตีแบบ Injection การโจมตี PHP SQL Injection ถือเป็นที่โดดเด่นที่สุด เว็บไซต์ส่วนใหญ่ร่อนแรงต่อการโจมตีนี้ ตามการวิจัย พบว่าเกิน 50% ของการโจมตีบนเว็บมาจาก SQL Injection ในบทความนี้ เราจะอธิบายวิธีการทำงานของ SQL Injection และวิธีป้องกันในแอปพลิเคชัน PHP

SQL Injection (SQLi) คืออะไร?

มาทำความเข้าใจ SQL Injection กันก่อน SQL Injection (SQLi) เป็นเทคนิคที่แฮกเกอร์เปลี่ยนแปลงคำสั่ง SQL ที่ทำงานด้านหลัง (backend) ผ่านการปลอมแปลงคำสั่ง SQL ที่ถูกส่งไปรัน การโจมตีแบบนี้มักทำผ่านช่องกรอกข้อมูล (input fields) ของฟอร์ม และสร้างผลกระทบร้ายแรงต่อฐานข้อมูล สุดท้ายนำไปสู่การเปิดเผยข้อมูลความลับจากฐานข้อมูล เนื่องจากแฮกเกอร์ได้รับการควบคุมฐานข้อมูลผ่านการ Inject คำสั่ง SQL จึงสามารถแทรก (insert), แก้ไข (update) หรือลบ (delete) ข้อมูลได้ตามต้องการ

การโจมตี SQL Injection เกิดขึ้นเมื่อโค้ดไม่ได้เขียนอย่างปลอดภ บางโค้ดดูเหมือนปลอดภัย แต่ก็ยังเปิดโอกาสให้ SQLi เกิดขึ้นได้:

  • การกรองตัวอักษร space และจัดการประเภทข้อมูล (type handling) ไม่ถูกต้อง
  • ส่งข้อมูลที่ไม่ได้ทำความสะอาด (unsanitized) หรือเข้ารหัส (encode) ไม่ถูกต้องไปยังฐานข้อมูล
  • การผสมโค้ดและข้อมูลไว้ด้วยกัน
  • การใช้เครื่องหมายอัญประกาศ (quotation marks) เพื่อจำกัดสตริง

สรุปคือ เมื่อ input ของผู้ใช้ไม่ได้รับการตรวจสอบ (validate) อย่างเหมาะสม จะทำให้ข้อมูลของผู้ใช้ถูกตีความโดยฐานข้อมูลอย่างไว้อันตราย หาก input ถูกดึงมาจาก frontend โดยตรงและนำไปใส่ใน query ก็จะนำไปสู่การถูกเจาะระบบ

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

PHP SQL Injection คืออะไร?

ตัวอย่างโค้ดที่ไม่ปลอดภัย

มาดูแนวคิดของ PHP SQL Injection ผ่านโค้ด PHP ที่มีช่องโหว่ด้านล่าง:

<?php
$username = $_POST['uname'];
$pass = $_POST['password'];
$sql = "SELECT * FROM user_table WHERE username='".$username."' AND userpwd='".$pass"';";
?>

โค้ดข้างต้นรับ input จากผู้ใช้โดยตรงโดยไม่มีการ validate และนำไปใส่ใน SQL query ที่จะรันบนฐานข้อมูลทันที แฮกเกอร์สามารถใส่เงื่อนไขตรรกะ (logical queries) ที่คืนค่า true ได้ เช่น ถ้าตัวแปร $password ได้ค่าเป็น ' OR '1'='1 เงื่อนไขจะกลายเป็น 1=1 ซึ่งเป็นจริงเสมอ นำไปสู่การ login สำเร็จโดยไม่ต้องรู้รหัสผ่าน จนเกิดการละลายความปลอดภัยร้ายแรง

พิจารณาสถานการณ์อีกกรณี ที่ error ถูกแสดงให้ผู้ใช้เห็น:

<?php
...
...
elseif($mysqli->error)
{
    print($mysqli->error);
}
?>

ในกรณีนี้ แทนที่จะพิมพ์ error log บนหน้าจอผู้ใช้ แนวทางที่ดีกว่าคือ เก็บ error log ไว้ในฐานข้อมูลแยกต่างหาก เพื่อนำไปตรวจสอบภายหลังว่ามีการโจมตี SQLi กี่ครั้ง

ตัวอย่างโค้ดที่ปลอดภัย

ตัวอย่างการเขียนโค้ดปลอดภัยเพื่อป้องกัน PHP SQL Injection โดยการ validate username และ password ก่อนนำไปใช้ใน query:

<?php
function checkUserInput($str)
{
    return str_replace(array("'","\"", "'", '"'), array("\\'","\\\"","\\'","\\\""), $str);
}
$username = checkUserInput($_POST['uname']);
$pass = checkUserInput($_POST['password']);
?>

วิธีนี้ช่วย validate input ก่อนที่จะนำไปต่อท้าย SQL query อีกแนวทางหนึ่งคือ ใช้ Prepared Statements (PDO - PHP Data Objects) ซึ่งเป็นวิธีที่มีประสิทธิภาพ สามารถรัน SQL statements ซ้ำๆ ได้ PDO มีเมธอดที่ทำให้ parameterized queries ใช้งานง่าย เพิ่มความอ่านง่ายและ portability ของโค้ด และทำงานกับไม่ได้แค่ MySQL แต่ฐานข้อมูลอื่นๆ ด้วย PDO จะกำหนดว่าส่วนไหนเป็น user input และส่วนที่เหลือเป็นคำสั่งฐานข้อมูล

ตัวอย่างโค้ด PDO สำหรับ MySQL:

// DB credentials
define('DB_HOST','localhost');
define('DB_USER','John_Reaper');
define('DB_PASS','admin@wsxdn.com@$$w0rd$');
define('DB_NAME','users');

if(isset($_GET['id']))
{
    $id = $_GET['id'];
    if(is_numeric($id) == true)
    {
        try
        {
            $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $q = "SELECT username FROM users WHERE id = :id";
            $sth = $dbh->prepare($q);
            $sth->bindParam(':id', $id);
            $sth->execute();
            $sth->setFetchMode(PDO::FETCH_ASSOC);
            $res = $sth->fetchColumn();
            print(htmlentities($res));
            $dbh = null;
        }
        catch(PDOException $e)
        {
            error_log('PDOException - '.$e->getMessage(), 0);
            http_response_code(500);
            die('Connection to database unsuccessful');
        }
    }
    else
    {
        http_response_code(400);
        die('Error processing malformed request');
    }
}

วิธีป้องกัน PHP SQL Injection

ดังนั้น คุณสามารถรักษาความปลอดภัยเว็บไซต์จาก SQL Injection โดยปฏิบัติตาม best practices ดังนี้:

  1. Validate user input ทั้ง frontend (JavaScript) และ backend (PHP)
  2. ไม่แสดง error ให้ผู้ใช้เห็น แต่ log error ไว้ในไฟล์ที่ attacker เข้าถึงไม่ได้ และ error message ที่แสดงให้ผู้ใช้ต้องเป็นข้อความทั่วไป ไม่เปิดเผยข้อมูลฐานข้อมูล
  3. ใช้ Parameterized Query เพื่อระบุว่าส่วนไหนของ SQL query ต้องถูกจัดการเป็น user input
  4. ลบ stored procedures ที่ไม่ได้ใช้ เพราะถูก exploit ได้ง่าย
  5. กำหนดสิทธิ์ (permissions) ให้เหมาะสมกับผู้ใช้แต่ละคน เพื่อป้องกันการถูกเจาะหาก attacker ผ่านการ authentication ไปได้

การป้องกัน PHP SQL Injection อย่างครบถ้วน

การใช้ Firewall อย่าง Astra จะเพิ่มภูมิคุ้มกันต่อ SQLi ได้อย่างมาก นอกจาก SQLi แล้ว Astra Security Suite ยังปกป้องเว็บไซต์จากอีก 100+ โจมตี เช่น bad bots, XSS, CSRF เป็นต้น

PHP SQL Injection แกะลึกลับ: ปกป้องแอปพลิเคชันจากการโจมตี

Malware Scanner ของเราสแกนเว็บไซต์เสร็จภายใน 10 นาที และสแกนรอบต่อไปใช้เวลาน้อยกว่า 1 นาที ภายใต้บริการ Security Audit และ Pentesting วิศวกรของ Astra จะสแกนทุกบรรทัดของโค้ดด้วยตนเอง เพื่อหาและแก้ไขช่องโหว่ ทำให้ความปลอดภัยของเว็บไซต์ของคุณแบบ impenetrable

PHP SQL Injection แกะลึกลับ: ปกป้องแอปพลิเคชันจากการโจมตี

ลอง Astra demo วันนี้!