2018年11月4日 星期日

《面試官別再問》PHP中$_FILES檔案上傳漏洞

當僅允許上傳圖片的時候,開發者通常使用PHP的getimagesize函數來檢測圖片的頭部信息.該函數在被調用時將會返回圖片的尺寸,如果圖片經驗證為無效的,也就是說圖片頭部信息不正確,則會返回false值.因此一個開發者一般會檢查該函數是否返回true或false,並且通過該信息來驗證上傳的文件.所以,如果一個惡意用戶試著上傳一個內嵌有簡單PHP shell的jpg文件的話,該函數會返回false然後他將不允許上傳此文件.然而,即使這種方式也能被很容易的繞過.如果一個圖片在一個圖片編輯器內打開,就如Gimp ,用戶就可以編輯圖片的註釋區,那兒就能插入PHP代碼,就如下圖所示.
該圖片仍然有一個有效的頭部; 因此就繞過了getimagesize 函數的檢查. 從下面截圖中可以看到, 當一個普通的web 瀏覽器請求該圖的時候, 插入到圖片註釋區的PHP 代碼仍然被執行了:


檔案上傳漏洞(File Upload Attack)是由於檔案上傳功能實現代碼沒有嚴格限制用戶上傳的檔案後綴以及文件類型,導致允許攻擊者向某個可通過Web 訪問的目錄上傳任意PHP檔案,並能夠將這些檔案傳遞給PHP 解釋器,就可以在遠程服務器上執行任意PHP腳本。

一套web應用程序,一般都會提供文件上傳的功能,方便來訪者上傳一些文件。
 下面是一個簡單的文件上傳表單

<form action="upload.php" method="post" enctype="multipart/form-data" name="form1">

<input type="file" name="file1" /><br />

<input type="submit" value="上傳文件" />

<input type="hidden" name="MAX_FILE_SIZE" value="1024" />

</form>


php的配置文件php.ini,其中選項upload_max_filesize指定允許上傳的文件大小,默認是2M
$_FILES數組變量
PHP使用變量$_FILES來上傳文件,$_FILES是一個數組。
如果上傳test.txt,那麼$_FILES數組的內容為:

$FILES

Array
{

  [file] => Array
   {

     [name] => test.txt //文件名稱
     [type] => text/plain //MIME類型
     [tmp_name] => /tmp/php5D.tmp //臨時文件
     [error] => 0 //錯誤信息
     [size] => 536 //文件大小,單位字節
   }
}

如果上傳文件按鈕的name屬性值為file
<input type="file" name="file" />
那麼使用$_FILES['file']['name']來獲得客戶端上傳文件名稱,不包含路徑。使用$_FILES['file']['tmp_name']來獲得服務端保存上傳文件的臨時文件路徑


存放上傳文件的文件夾

PHP不會直接將上傳文件放到網站根目錄中,而是保存為一個臨時文件,名稱就是$_FILES['file']['tmp_name']的值,開發者必須把這個臨時文件複製到存放的網站文件夾中。

$_FILES['file']['tmp_name']的值是由PHP設置的,與文件原始名稱不一樣,開發者必須使用$_FILES['file']['name']來取得上傳文件的原始名稱。
上傳文件時的錯誤信息 $_FILES['file']['error']變量用來保存上傳文件時的錯誤信息,它的值如下:

如果提供給網站訪問者上傳圖片的功能,那必須小心訪問者上傳的實際可能不是圖片,而是可以指定的PHP程序。如果存放圖片的目錄是一個開放的文件夾,則入侵者就可以遠程執行上傳的PHP文件來進行攻擊。
下面是一個簡單的文件上傳例子:
<?php
// 設置上傳文件的目錄
$uploaddir = "D:/www/images/";

// 檢查file是否存在
if (isset($_FILES['file1']))

{
// 要放在網站目錄中的完整路徑,包含文件名
$uploadfile = $uploaddir . $_FILES['file1']['name'];

// 將服務器存放的路徑,移動到真實文件名 move_uploaded_file($_FILES['file1']['tmp_name'], $uploadfile);

}
?>
……
<form method="post" enctype="multipart/form-data" name="form1">
<input type="file" name="file1" /><br />
<input type="submit" value="上傳文件" />
<input type="hidden" name="MAX_FILE_SIZE" value="1024" />
</form>

這個例子沒有檢驗文件後綴,可以上傳任意文件,很明顯的上傳漏洞。
利用此漏洞黑客克制自由上床任意的木馬文件而導致網站失陷。


漏洞防護措施

解決上面所述問題的一種方法是通過檢查上傳文件的類型來限制用戶的文件上傳,如以下代碼所示。


<?php

if(isset($_POST["form"]))


{
if($_FILES['upfile']['type'] == 'image/pjpeg') //檢查文件類型是否為JPEG

{
$uploadfile = "upfiles/".$_FILES['upfile']['name'];
//上傳後文件所在的文件名和路徑


move_uploaded_file($_FILES['upfile']['tmp_name'], $uploadfile);

//上傳文件



print_r($_FILES);
die();
}


else
   {
     die("上傳文件的格式不正確!");
   }
}
?>

上面的代碼要求用戶上傳的文件必須是JPEG類型的圖片文件,徹底地避免了終端用戶通過上傳PHP腳本危害服務器的行為。

沒有留言:

張貼留言