PHP漏洞中的戰爭

來源:互聯網
上載者:User
關鍵字 PHP漏洞中的戰爭

濫用include
1.漏洞原因:
Include是編寫php網站中最常用的函數,並且支援相對路徑。有很多PHP指令碼直接把某輸入變數作為Include的參數,造成任意引用指令碼、絕對路徑泄露等漏洞。看以下代碼:
...
$includepage=$_GET["includepage"];
include($includepage);
...
很明顯,我們只需要提交不同的Includepage變數就可以獲得想要的頁面。如果提交一個不存在的頁面,就可以使PHP指令碼發生錯誤而泄露實際絕對路徑(這個問題的解決辦法在下面的文章有說明)。
2.漏洞解決:
這個漏洞的解決很簡單,就是先判斷頁面是否存在再進行Include。或者更嚴格地,使用數組對可Include的檔案作出規定。看以下代碼:
$pagelist=array("test1.php","test2.php","test3.php"); //這裡規定可進行include的檔案
if(isset($_GET["includepage"])) //判斷是否有$includepage
{
$includepage=$_GET["includepage"];
foreach($pagelist as $PRepage)
{
if($includepage==$prepage) //檢查檔案是否在允許清單中
{
include($prepage);
$checkfind=true;
break;
}
}
if($checkfind==true){ unset($checkfind); }
else{ die("無效引用頁!"); }
}
這樣就可以很好地解決問題了。

小提示:有此問題的函數還有:require(),require_once(),include_once(),readfile()等,在編寫的時候也要注意。

未對輸入變數進行過濾
1.漏洞原因:
這個漏洞早在asp中出現過,當時造成的注入漏洞不計其數。但由於PHP在當時的影響力較小,所以沒有太多的人能夠注意這點。對於PHP來說,這個漏洞的影響性比ASP更大,因為有比較多的PHP指令碼使用到文本型資料庫。當然也存在SQL語句的注入問題。舉個比較經典的例子,首先是資料庫的:
$id=$_GET["id"];

$query="SELECT * FROM my_table where id='".$id."'"; //很經典的SQL注入漏洞
$result=MySQL_query($query);
這裡很明顯我們可以用注入來獲得資料庫的其它內容了。這裡就不再詳細敘述,和ASP注入一樣的,大家可以看看以前的黑防。然後我們看文本資料庫的問題:
$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];

$fd=fopen("test.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);
文本的漏洞可以說是更加嚴重。倘若我們的提交的變數中插入一段很小的PHP代碼,就可以另這個文本資料庫test.php變成PHP後門。甚至插入上傳代碼,讓我們可以上傳一個完善的PHP後門。接著提升許可權,伺服器就是你的了。
2.漏洞解決:
這個漏洞的解決方案其實很簡單,就是嚴格對全部提交的變數進行過濾。對一些敏感的字元進行替換。我們可以藉助PHP提供的htmlspecialchars()函數來替換HTML的內容。這裡給出一段例子:
//構造過濾函數 www.cncms.com
function flt_tags($text)
{
$badWords=array("操你媽","fuck"); //詞彙過濾列表
$text=rtrim($text);
foreach($badwords as $badword) //這裡進行詞彙的過濾
{
if(stristr($text,$badword)==true){ die("錯誤:你提交的內容含有敏感字眼,請不要提交敏感內容。"); }
}
$text=htmlspecialchars($text); //HTML替換
//這兩行把斷行符號替換為

$text=str_replace("\r","
",$text);
$text=str_replace("\n","",$text);
$text=str_replace("&line;","│",$text); //文本資料庫分隔字元"&line;"替換為全形的"│"
$text=preg_replace("/\s{ 2 }/"," ",$text); //空格替換
$text=preg_replace("/\t/"," ",$text); //還是空格替換
if(get_magic_quotes_gpc()){ $text=stripslashes($text); } //如果magic_quotes開啟,則進行\'的替換
return $text;
}

$text1=$_POST["text1"];
$text2=$_POST["text2"];
$text3=$_POST["text3"];

//過濾全部輸入
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3);

$fd=fopen("test.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);
經過一番替換和過濾後,你就可以安全地把資料寫入文本或資料庫了。

管理員判斷不完全
1.漏洞原因:
我們用PHP寫指令碼,通常要涉及管理員的許可權問題。而一些指令碼僅僅對管理員權限作出"是"判斷,而往往忽略了"否"判斷。在PHP設定檔中register_globals開啟的情況下(4.2.0以後版本預設關閉,但有不少人為了方便而開啟它,這是極度危險的行為),就會出現提交變數冒充管理員的情況。我們看一下的例子代碼:
$cookiesign="admincookiesign"; //判斷是否Admin的cookie變數
$adminsign=$_COOKIE["sign"]; //擷取使用者的cookie變數

if($adminsign==$cookiesign)
{
$admin=true;
}

if($admin){ echo "現在是管理員狀態。"; }
看上去好像很安全的樣子,呵呵。現在我們假設PHP設定檔中register_globals為開啟狀態。我們提交這樣一個地址“test.php?admin=true”,結果看到了嗎?我們雖然沒有正確的Cookie,但由於register_globals為開啟狀態,使得我們提交的admin變數自動註冊為true。而且指令碼缺少“否”判斷,就使得我們順利地通過admin=true取得管理員的許可權了。這個問題存在於大部分網站和論壇當中。
2.漏洞解決:
解決這個問題,我們只需要在指令碼中加入對管理員的“否”判斷即可。我們仍然假設PHP設定檔中register_globals為開啟狀態。看一下的代碼:
$cookiesign="admincookiesign"; //判斷是否Admin的cookie變數
$adminsign=$_COOKIE["sign"]; //擷取使用者的cookie變數

if($adminsign==$cookiesign)
{
$admin=true;
}
else
{
$admin=false;
}
if($admin){ echo "現在是管理員狀態。"; }
這樣,就算攻擊者在沒有正確Cookie的情況下提交了admin=true的變數,指令碼在以後的判斷中也會把$admin設定為False。這樣就解決了部分的問題。但由於$admin是變數,倘若在以後的其他指令碼引用中出現了漏洞使得$admin被重新賦值就會引發新的危機。因此,我們應該使用常量來存放管理員權限的判定。使用Define()語句定義一個admin常量來記錄管理員許可權,在此以後若配重新賦值就會出錯,達到保護的目的。看以下代碼:
$cookiesign="admincookiesign"; //判斷是否Admin的cookie變數
$adminsign=$_COOKIE["sign"]; //擷取使用者的cookie變數

if($adminsign==$cookiesign)
{
define(admin,true);
}
else
{
define(admin,false);
}
if(admin){ echo "現在是管理員狀態。"; }
值得注意的是,我們使用了Define語句,所以在調用Admin常量時前面不要習慣性的加變數符號$,而應該使用Admin和!admin。

文本資料庫暴露
1.漏洞原因:
前面已經說過,由於文本資料庫具有很大的靈活性,不需要任何外部支援。加上PHP對檔案的處理能力十分強,因此文本資料庫在PHP指令碼中的應用甚廣。甚至有幾個很好的論壇程式就是使用文本資料庫的。但有得必有失,文本資料庫的安全性也是比其他資料庫要低的。
2.漏洞解決:
文本資料庫作為一個普通的檔案,它可以被下載,就像MDB一樣。所以我們要用保護MDB的辦法來保護文本資料庫。把文本資料庫的尾碼名改為.PHP。並在資料庫的第一行加入。這樣文本資料庫就會作為一個PHP檔案,並且在第一行退出執行。也就是返回一個空頁面,從而達到保護文本資料庫的目的。

錯誤路徑泄露
1.漏洞原因:
PHP遇到錯誤時,就會給出出錯指令碼的位置、行數和原因,例如:
Notice: Use of undefined constant test - assumed 'test' in D:\interpub\bigfly\test.php on line 3
有很多人說,這並沒有什麼大不了。但泄露了實際路徑的後果是不堪設想的,對於某些入侵者,這個資訊可是非常重要,而事實上現在有很多的伺服器都存在這個問題。
有些網管乾脆把PHP設定檔中的display_errors設定為Off來解決,但本人認為這個方法過於消極。有些時候,我們的確需要PHP返回錯誤的資訊以便調試。而且在出錯時也可能需要給使用者一個交待,甚至導航到另一頁面。
2.漏洞解決:
PHP從4.1.0開始提供了自訂錯誤處理控制代碼的功能函數set_error_handler(),但很少數指令碼編寫者知道。在眾多的PHP論壇中,我只看見很少一部分對此情況進行了處理。set_error_handler的使用方法如下:
string set_error_handler ( callback error_handler [, int error_types])
現在我們就用自訂的錯誤處理把實際路徑過濾掉。
//admin為管理員的身份判定,true為管理員。
//自訂的錯誤處理函數一定要有這4個輸入變數$errno,$errstr,$errfile,$errline,否則無效。
function my_error_handler($errno,$errstr,$errfile,$errline)
{
//如果不是管理員就過濾實際路徑
if(!admin)
{
$errfile=str_replace(getcwd(),"",$errfile);
$errstr=str_replace(getcwd(),"",$errstr);
}

switch($errno)
{
case E_ERROR:
echo "ERROR: [ID $errno] $errstr (Line: $errline of $errfile)
\n";
echo "程式已經停止運行,請聯絡管理員。";
//遇到Error級錯誤時退出指令碼
exit;
break;

case E_WARNING:
echo "WARNING: [ID $errno] $errstr (Line: $errline of $errfile)
\n";
break;

default:
//不顯示Notice級的錯誤
break;
}
}

//把錯誤處理設定為my_error_handler函數
set_error_handler("my_error_handler");

這樣,就可以很好地解決安全和調試方便的矛盾了。而且你還可以花點心思,使錯誤提示更加美觀以配合網站的風格。不過注意兩點是:
(1)E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING是不會被這個控制代碼處理的,也就是會用最原始的方式顯示出來。不過出現這些錯誤都是編譯或PHP核心出錯,在通常情況下不會發生。
(2)使用set_error_handler()後,error_reporting ()將會失效。也就是所有的錯誤(除上述的錯誤)都會交給自訂的函數處理。
其它有關於set_error_handler()的資訊,大家可以參考PHP的官方手冊。

POST漏洞
1.漏洞原因:
前面已經說過,依靠register_globals來註冊變數是個不好的習慣。在一些留言本和論壇程式中,更要嚴格檢查獲得頁面的方式和提交的時間間隔。以防止灌水式發帖和外部提交。我們看一下以下某留言本程式的代碼:
...
$text1=flt_tags($text1);
$text2=flt_tags($text2);
$text3=flt_tags($text3);

$fd=fopen("data.php","a");
fwrite($fd,"\r\n$text1&line;$text2&line;$text3");
fclose($fd);
...
很明顯的,如果我們提交網址”post.php?text1=testhaha&text2=testhaha&text3=testhaha”。資料就會被正常寫入檔案中。此程式並沒有檢測變數的來源和瀏覽器獲得頁面的方式。如果我們向這個頁面重複多次提交,就會起到洪水的作用。現在也有一些軟體利用這個漏洞來在論壇或留言本上發廣告,這是可恥的行為(我朋友的留言本就在1星期內被灌了10多頁,無奈)。
2.漏洞解決:
在進行資料處理和儲存前,首先判斷瀏覽器的獲得頁面方式。使用$_SERVER["REQUEST_METHOD"]變數來獲得瀏覽器的獲得頁面方式。檢查其是否為”POST”。在指令碼中使用session來記錄使用者是否通過正常途徑(即填寫提交內容的頁面)來提交資料。或使用$_SERVER["HTTP_REFERER"]來檢測,但不推薦這樣做。因為部分瀏覽器沒有設定REFERER,有部分防火牆也會屏蔽REFERER。另外,我們也要對提交內容檢查,看資料庫中是否有重複內容。以留言本為例,使用Session進行判定:
填寫瀏覽內容的頁面中,我們在最前端加上:
$_SESSION["allowgbookpost"]=time(); //登記填寫時的時間
在接受留言資料並儲存的頁面中我們在進行資料處理前我們也用Session進行以下處理:
if(strtoupper($_SERVER["REQUEST_METHOD"])!=”POST”){ die("錯誤:請勿在外部提交。"); } //檢查頁面獲得方法是否為POST
if(!isset($_SESSION["allowgbookpost"]) or (time()-$_SESSION["allowgbookpost"] < 10)){ die("錯誤:請勿在外部提交。"); } //檢查留言填寫時的時間
if(isset($_SESSION["gbookposttime"]) and (time()-$_SESSION["gbookposttime"] < 120)){ die("錯誤:兩次提交留言的間隔不得少於 2 分鐘。"); } //檢查留言間隔

unset($_SESSION["allowgbookpost"]); //登出allowgbookpost變數以防止一次進入填寫頁面多次進行提交
$_SESSION["gbookposttime"]=time(); //登記發送留言的時間,防止灌水或惡意攻擊
...
資料處理及儲存
...
經過這樣重重審查,你的程式就安全很多了。

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.