เพิ่มความปลอดภัยให้กับ PHP ของคุณ (ตอนที่ 2 : ความปลอดภัยที่มักถูกมองข้าม)

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

Register Globals

ปัญหานี้เป็นปัญหาที่พบได้มากเลยทีเดียวครับ และก็ไม่เห็นว่าจะมีใครสนใจมันเลย นับตั้งแต่ PHP เวอร์ชั่น 4.2.0 หรืออะไรประมาณนี้เนี่ยแหละ ค่า register_globals ใน php.ini ก็ถูกตั้งไว้ที่ Off โดยปกติ ซึ่งก็สร้างปัญหาพอสมควรกับผู้เขียนโค๊ดทั่วไป ที่มักจะเรียกใช้ข้อมูลจากผู้ใช้แบบง่ายๆ ประมาณว่าการใช้ $foo ในการเรียกข้อมูลมาจาก form หรือ url ที่เป็น ?foo=bar นะครับ เพราะเรากำลังส่งข้อมูลจากภายนอกเข้ามารวมกับสคริปต์ของเรา ทางแก้ไขก็คือการตั้ง register_globals เป็น Off แล้วเรียกใช้ข้อมูลจากภายนอก ดังนี้

  • เวลารับข้อมูลมาจาก ฟอร์ม ในหน้าก่อนๆ ให้ใช้ $_POST['foo']
  • เวลารับข้อมูลมาจาก URL เช่น http://localhost/test.php?foo=bar ก็ให้ใช้ $_GET['foo']
  • เวลารับข้อมูลจาก Session ให้ใช้ $_SESSION['foo']

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

การกลั่นกรองข้อมูล

สามสิ่งที่ควรคำนึง...

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

วิธีที่ 1 : การแจกจ่ายงาย

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

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

หลักการก็ง่ายๆเลย ไฟล์ index.php มีหน้าที่ในการรับข้อมูลมาจากผู้ที่เรียกใช้งาน โดยที่มีข้อมูลมาเรียบร้อย จากนั้นตรวจสอบดูว่าผู้ใช้ต้องการทำงานอะไร แล้วค่อยไปดูดไฟล์ที่เกี่ยวข้องออกมาประมวลผล ดังตัวอย่างสำหรับการใช้ URI เช่น http://localhost/index.php?task=showrecord&name=tuwannu

<?
switch($_GET['task']) {
      case 'showrecord' :
            $name = addslashes($_GET['name']);
 
            include './system/record.php';
      break;
 
      case 'showcategory' :
 
            include './system/category.php';
      break;
 
      default:
 
            echo 'No Task';
      break;
}
?>

อธิบายโค๊ดนะครับ คือพอผู้ใช้เรียกไฟล์ index.php ขึ้นมา โดยระบุ task กับ name มา เราก็จะนำ task ไปเทียบกับข้อมูลในระบบ ในที่นี้ task จะเป็น showrecord พอโค๊ดตรวจสอบพบว่ามี task นี้อยู่ ก็จะตรวจสอบข้อมูลใน name ต่อ โดยในส่วนนี้อาจจะเป็นการ addslashes() เพื่อตัดส่วนที่เป็นอันตรายต่อระบบออกไป พอตรวจสอบเสร็จแล้ว ก็จะไปเรียกไฟล์ใน ./system/record.php ขึ้นมา (โดยที่โฟลเดอร์ system อาจจะกำหนดการเข้าถึงไว้ ทำให้ผู้ใช้ทั่วไปเข้าไปใช้โดยตรงไม่ได้) เป็นการป้องกันข้อมูลขึ้นมาอีกชั้นนึงครับผม หากหา task ไม่พบ ก็จะให้ขึ้นว่า No Task พูดง่ายๆ คือจำกัดว่าผู้ใช้สามารถทำได้แค่ showrecord หรือ showcategory แค่นี้แหละ

ทางเลือกที่สอง

ถ้าหากการรวมทุกอย่างมะรุมมะตุ้มอยู่หน้าเดียวกันจะเป็นการไม่สะดวกเกินไป ใช้ include() แทนก็ได้ครับ โดยการแยกไฟล์ที่เกี่ยวข้องกับการรักษาความปลอดภัยออกไปเป็นอีกส่วนหนึ่ง พอจะใช้ก็ include() ไฟล์นั้นเข้ามา วิธีนี้ก็เวิร์คเหมือนกัน แต่ทีนี้ก็ต้องระมัดระวังครับว่า include() ลงไปในทุกไฟล์ที่เกี่ยวข้อง หรือถ้าคุณสามารถกำหนด php.ini ได้ ก็ลองเปลี่ยน auto_prepend_file ดู ซึ่งเป็นการกำหนดว่าให้ PHP เรียกใช้ไฟล์ไหนโดยอัตโนมัติ จะได้ไม่ต้องห่วงเรื่องการไม่ได้ include() ไฟล์ลงในหน้าใดหน้าหนึ่ง

ตั้งชื่อให้น้องคนใหม่

หลังจากการตรวจสอบข้อมูลในบางครั้ง เราอาจจะมีการแปลงข้อมูลเล็กน้อยเพื่อให้เป็นข้อมูลที่ปลอดภัย เช่น addslashes() แต่พอประมวลผลเสร็จ ขอแนะนำให้นำข้อมูลแยกออกมาเป็นอีกไฟล์นึงซึ่งไม่เกี่ยวกับ $_GET หรือ $_POST อย่าทำพวก

<?
$_GET['name'] = addslashes($_GET['name']);
?>

เพื่อเป็นการแยกแยะระหว่างข้อมูลดิบที่ได้มาจากผู้ใช้ และข้อมูลที่สามารถใช้ได้อย่างปลอดภัย

Error Reporting

  • error_reporting ควรจะตั้งไว้ที่ E_ALL เนื่องจากตัว PHP จะทำการแสดงคำเตือนทั้งหมด เหมาะสำหรับเวลาที่ต้องการ debug ข้อมูล เป็นการทำความแน่ใจว่าสคริปต์ของคุณมีความถูกต้องมากที่สุด
  • display_errors ควนจะตั้งเป็น On ในช่วงพัฒนาโค๊ด เพื่อเป็นการเปิดเผยคำเตือนต่างๆที่เกิดขึ้นในการพัฒนา ซึ่งจะช่วยในการแก้ไขโค๊ดเป็นไปได้ง่ายขึ้น ส่วนเวลาที่เริ่มนำโค๊ดไปใช้จริง ก็เปลี่ยนให้เป็น Off ซะ กันเผื่อว่าคนทั่วไปจะเห็นข้อผิดพลาดภายในเว็บ
  • log_errors ควรตั้งเป็น On ตลอดเวลาเพื่อตรวจจับความผิดพลาดที่เกิดขึ้นในเว็บ โดยที่จะมี log เก็บไว้ในไฟล์ ตามที่ตั้งไว้สำหรับ error_logs

Post new comment

คำนวณผลบวกด้านบนแล้วกรอกผลลัพธ์ลงในช่อง เช่น 2 ลบ 1 ให้พิมพ์ 1