เจาะลึกช่องโหว่ Insecure Deserialization ใน PHP: เมื่อ Cache กลายเป็นจุดอ่อนที่แฮกเกอร์จ้องเล่นงาน

8 นาที 17 views บันทึกเป็น PDF
เจาะลึกช่องโหว่ Insecure Deserialization ใน PHP: เมื่อ Cache กลายเป็นจุดอ่อนที่แฮกเกอร์จ้องเล่นงาน

รู้ไหมว่าการเก็บข้อมูลแบบ Serialization อาจเป็นช่องโหว่ให้แฮกเกอร์เจาะระบบได้ มาทำความเข้าใจวิธีป้องกัน Insecure Deserialization ใน PHP ให้ปลอดภัยกัน

Insecure Deserialization คืออะไรและทำไมต้องระวัง

เวลาเราเขียนโปรแกรม เรามักต้องเก็บข้อมูลของวัตถุ (Object) ไว้ในฐานข้อมูลหรือไฟล์ เพื่อเอามาใช้งานต่อได้ในภายหลัง กระบวนการที่เปลี่ยนวัตถุให้กลายเป็นข้อความตัวอักษรยาวๆ เพื่อให้ง่ายต่อการเก็บหรือส่งต่อ เราเรียกว่า Serialization (การแปลงข้อมูลให้เป็นรูปแบบที่ส่งผ่านได้ง่าย)

ส่วนกระบวนการย้อนกลับ คือการเอาข้อความเหล่านั้นมาเปลี่ยนคืนเป็นวัตถุให้โปรแกรมใช้งานได้เหมือนเดิม เราเรียกว่า Deserialization (การแปลงข้อมูลกลับมาเป็นวัตถุ) มันเหมือนกับการแกะกล่องพัสดุที่ถูกห่อมาอย่างดี เพื่อเอาของข้างในออกมาใช้งานนั่นเอง

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

รู้จักกับ Serialization ใน PHP

ในภาษา PHP เรามีฟังก์ชันหลักสองตัวที่ใช้จัดการเรื่องนี้ คือ serialize() สำหรับแปลงวัตถุเป็นข้อความ และ unserialize() สำหรับแปลงข้อความกลับเป็นวัตถุ ข้อมูลที่ได้จะเป็นรูปแบบพิเศษที่ระบุชื่อคลาส (Class - ต้นแบบของวัตถุ) และค่าตัวแปรต่างๆ ไว้ข้างใน

ลองนึกภาพว่าเรามีข้อมูลผู้ใช้งานที่ชื่อ "John" ระบบจะแปลงเป็นข้อความหน้าตาแบบนี้ O:4:"User":2:{s:4:"name";s:4:"John"...} เมื่อโปรแกรมเรียกใช้ unserialize() ระบบจะอ่านข้อความนี้ แล้วสร้างวัตถุจากคลาส User ขึ้นมาใหม่โดยอัตโนมัติพร้อมใส่ชื่อ John ให้ทันที

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

// ตัวอย่างการทำ Serialization
$user = new User();
$user->name = "John";
$data = serialize($user);
echo $data; // ผลลัพธ์: O:4:"User":1:{s:4:"name";s:4:"John";}

// ตัวอย่างการทำ Deserialization
$raw = 'O:4:"User":1:{s:4:"name";s:4:"John";}';
$obj = unserialize($raw);
echo $obj->name; // ผลลัพธ์: John

บรรทัดแรกเราสร้างวัตถุ User ขึ้นมา ส่วนบรรทัดที่สองแปลงเป็นข้อความเพื่อจัดเก็บ ต่อมาฟังก์ชัน unserialize() จะอ่านข้อความนั้นแล้วเปลี่ยนกลับเป็นวัตถุ User ให้เราใช้งานต่อได้ตามปกติ

Magic Methods: จุดเชื่อมต่อที่โจรชอบใช้

ใน PHP มีสิ่งที่เรียกว่า Magic Methods (ฟังก์ชันวิเศษที่ทำงานอัตโนมัติ) ซึ่งเป็นฟังก์ชันที่ถูกเรียกใช้โดยตัวภาษาเองในจังหวะต่างๆ เช่น ตอนที่วัตถุถูกสร้างขึ้น หรือตอนที่วัตถุถูกทำลายทิ้งไป ตัวอย่างเช่น __destruct() ที่จะทำงานทันทีเมื่อโปรแกรมจบการทำงาน

ถ้าคนร้ายสามารถควบคุมข้อมูลที่ถูก unserialize() ได้ เขาจะสามารถกำหนดค่าตัวแปรในวัตถุนั้นได้ และเมื่อ Magic Methods ทำงาน มันจะใช้ค่าตัวแปรที่คนร้ายตั้งไว้ไปทำคำสั่งต่างๆ ทันที นี่คือสิ่งที่เรียกว่าการ "ป้อนข้อมูลเพื่อควบคุมการทำงาน" ของโปรแกรม

ลองจินตนาการว่ามีคลาสที่เขียนไฟล์ลงเครื่องโดยอัตโนมัติในฟังก์ชัน __destruct() หากคนร้ายส่งข้อมูลที่ระบุชื่อไฟล์เป็น shell.php (ไฟล์ที่สั่งรันคำสั่งได้) เข้ามา โปรแกรมของเราก็จะเผลอสร้างไฟล์อันตรายนี้ขึ้นมาให้คนร้ายเองโดยไม่ตั้งใจ

class FileLogger {
    public $logFile;
    public function __destruct() {
        // บรรทัดนี้จะทำงานเองเมื่อจบโปรแกรม
        file_put_contents($this->logFile, "log data");
    }
}
// หากคนร้ายส่ง $logFile = 'hacker.php' เข้ามา
// ระบบจะสร้างไฟล์ hacker.php ให้ทันที

โค้ดตัวอย่างนี้แสดงให้เห็นว่าฟังก์ชัน __destruct() จะทำงานเองอัตโนมัติ หากเรานำข้อมูลจากผู้ใช้มาใส่ใน $logFile โดยตรง คนร้ายจะสามารถเลือกเขียนไฟล์ไปที่ไหนก็ได้บนเซิร์ฟเวอร์ของเรา

POP Chains: การต่อจิ๊กซอว์เพื่อบุกรุก

ในโลกความเป็นจริง ช่องโหว่ไม่ได้เกิดขึ้นจากคลาสเดียวเสมอไป แต่มันคือการใช้ POP Chains (Property Oriented Programming Chains - การนำคลาสหลายตัวมาเชื่อมต่อกันเพื่อสร้างคำสั่งอันตราย) คนร้ายจะนำคลาสหลายๆ ตัวในระบบมาต่อกันเหมือนจิ๊กซอว์ เพื่อพาไปสู่เป้าหมายที่ใหญ่กว่า

คนร้ายจะเริ่มจากคลาสหนึ่งที่ดูเหมือนไม่มีพิษมีภัย แล้วตั้งค่าตัวแปรให้มันไปเรียกฟังก์ชันในอีกคลาสหนึ่งต่อกันไปเรื่อยๆ จนกระทั่งไปถึงจุดที่สามารถสั่งรันคำสั่งในระบบเซิร์ฟเวอร์ได้ นี่คือเหตุผลว่าทำไมเราถึงต้องระวังทุกคลาสในโปรเจกต์ ไม่ใช่แค่คลาสที่รับข้อมูลโดยตรง

การป้องกัน POP Chains ทำได้ยากเพราะมันอาศัยความสัมพันธ์ของคลาสต่างๆ ที่เราเขียนไว้เอง ดังนั้นวิธีที่ดีที่สุดคือการไม่ยอมให้ข้อมูลจากผู้ใช้เข้าไปสู่ฟังก์ชัน unserialize() ตั้งแต่แรก หากไม่จำเป็นจริงๆ ให้ใช้รูปแบบข้อมูลที่ปลอดภัยกว่าแทน เช่น JSON (รูปแบบข้อมูลมาตรฐานที่อ่านง่ายและปลอดภัยกว่า)

วิธีป้องกันตัวเองในฐานะโปรแกรมเมอร์

กฎเหล็กข้อแรกและข้อสำคัญที่สุดคือ ห้ามใช้ unserialize() กับข้อมูลที่มาจากผู้ใช้โดยตรง หากจำเป็นต้องเก็บข้อมูลหรือส่งข้อมูล ให้เปลี่ยนไปใช้ json_encode() และ json_decode() แทน เพราะ JSON ไม่มีการเรียกใช้ Magic Methods หรือการสร้างวัตถุโดยอัตโนมัติ

หากคุณจำเป็นต้องใช้ unserialize() จริงๆ ให้ทำการ Sign (ลงลายเซ็นดิจิทัล) ข้อมูลนั้นด้วยรหัสลับก่อนเก็บ เมื่ออ่านข้อมูลกลับมา ต้องตรวจสอบลายเซ็นก่อนเสมอว่าข้อมูลถูกแก้ไขไปหรือไม่ หากข้อมูลไม่ตรงกับลายเซ็น ให้ปฏิเสธการใช้งานทันที

สุดท้ายคือการอัปเดต PHP ให้เป็นเวอร์ชันล่าสุดอยู่เสมอ และคอยตรวจสอบ Library (ชุดคำสั่งเสริม) ที่เรานำมาใช้ในโปรเจกต์ เพราะบางครั้งช่องโหว่อาจจะไม่ได้มาจากโค้ดที่เราเขียนเอง แต่มาจากเครื่องมือภายนอกที่เราดึงเข้ามาใช้งานนั่นเอง

สรุป: เปลี่ยนนิสัยเพื่อความปลอดภัย

การเข้าใจเรื่อง Insecure Deserialization จะช่วยให้คุณมองเห็นว่าข้อมูลทุกอย่างที่ผู้ใช้ส่งเข้ามาคือความเสี่ยง คุณต้องคิดเสมอว่าข้อมูลเหล่านั้นอาจถูกแก้ไขและนำมาหลอกล่อให้โปรแกรมของคุณทำงานผิดพลาดได้เสมอ

วิธีนำไปใช้จริง: เมื่อต้องเก็บข้อมูลสถานะผู้ใช้ (Session) หรือค่าสถานะต่างๆ ให้เปลี่ยนมาใช้ JSON แทนการ Serialize วัตถุโดยตรง หากต้องทำโปรเจกต์ส่งอาจารย์หรือทำงานจริง ให้ตรวจเช็คทุกครั้งว่าเราได้ทำลายจุดเสี่ยงเหล่านี้ไปแล้วหรือยัง

จำไว้ว่าโปรแกรมเมอร์ที่เก่งไม่ได้วัดกันแค่เขียนโค้ดได้เร็ว แต่ต้องเขียนโค้ดที่ "ปลอดภัย" ด้วย การฝึกฝนตั้งคำถามกับโค้ดตัวเองว่า "ถ้าคนร้ายแก้ไขค่านี้ได้ จะเกิดอะไรขึ้น" คือก้าวสำคัญที่จะทำให้คุณเป็นนักพัฒนาที่มีคุณภาพและน่าเชื่อถือในสายงานนี้ครับ


ที่มา: Insecure Deserialization in PHP — When Your Cache Becomes an Attack Surface — DEV Community: laravel

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

จัดการเซิร์ฟเวอร์ผ่าน VS Code ให้ง่ายขึ้นด้วย Easy SSH พร้อมฟีเจอร์โหลดไฟล์ผ่านคลิกเดียว

เบื่อไหมที่ต้องสลับหน้าจอไปมาเพื่อจัดการเซิร์ฟเวอร์? มาลองใช้ Easy SSH ปลั๊กอิน VS Code ที่ช่วยให้คุณรีโมทผ่าน Terminal ได้สะดวก แถมโหลดไฟล์ได้ง่ายแค่กด Ctrl+click

ที่มา: DEV Community

6 hours ago 11 นาที
3 views
วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

วิธีดึงข้อมูลราคาจาก Google Hotels ด้วย API สำหรับนักพัฒนา

อยากทำแอปท่องเที่ยวแต่ดึงข้อมูลราคาจาก Google Hotels ไม่ได้? มาดูวิธีใช้ Apify Actor ช่วยดึงข้อมูลแบบอัตโนมัติด้วย Python ง่ายๆ ไม่ต้องกลัวเว็บพัง

ที่มา: DEV Community

9 hours ago 8 นาที
5 views
วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

วิธีเช็กความพร้อมโปรเจกต์ก่อนปล่อยงานจริงด้วย ReleaseReady

เคยไหม? โค้ดรันได้ในเครื่องแต่พอปล่อยจริงกลับพัง! มาดูวิธีตรวจสอบความพร้อมของโปรเจกต์ก่อนอัปขึ้น GitHub ด้วยเครื่องมือ ReleaseReady กัน

ที่มา: DEV Community

13 hours ago 9 นาที
6 views