(轉) 加強PHP安全性的幾個原則

  • 1

加強PHP安全性的幾個原則

作為PHP程式師,特別是新手,對於互聯網的險惡總是知道的太少,對於外部的入侵有很多時候是素手無策的,他們根本不知道駭客是如何入侵的、提交入侵、上傳漏洞、sql 注入、跨腳本攻擊等等。作為最基本的防範你需要注意你的外部提交,做好第一面安全機制處理防火牆。

規則 1:絕不要信任外部資料或輸入
  關於Web應用程式安全性,必須認識到的第一件事是不應該信任外部資料。外部資料(outside data) 包括不是由程式師在PHP代碼中直接輸入的任何資料。在採取措施確保安全之前,來自任何其他來源(比如 GET變數、表單 POST、資料庫、設定檔、會話變數或Cookie)的任何資料都是不可信任的。
  例如,下面的資料元素可以被認為是安全的,因為它們是在PHP中設置的。
清單 1. 安全無暇的代碼   
$myUsername = ‘tmyer’;   
$arrayarrayUsers = array(‘tmyer’, ‘tom’, ‘tommy’);   
define(“GREETING”, ‘Hello there’ . $myUsername);   
?>  
  但是,下面的資料元素都是有瑕疵的。
清單 2. 不安全、有瑕疵的代碼   
$myUsername = $_POST['username']; //tainted!   
$arrayarrayUsers = array($myUsername, ‘tom’, ‘tommy’); //tainted!   
define(“GREETING”, ‘hello there’ . $myUsername); //tainted!   
?>  
  為什麼第一個變數$myUsername 是有瑕疵的?因為它直接來自表單POST。使用者可以在這個輸入域中輸入任何字串,包括用來清除檔或運行以前上傳的檔的惡意命令。您可能會問,難道不能使用只接受字母A-Z 的用戶端(Javascrīpt)表單檢驗腳本來避免這種危險嗎?”是的,這總是一個有好處的步驟,但是正如在後面會看到的,任何人都可以將任何表單下載到自己的機器上,修改它,然後重新提交他們需要的任何內容。
  解決方案很簡單:必須對$_POST['username'] 運行清理代碼。如果不這麼做,那麼在使用$myUsername的任何其他時候(比如在陣列或常量中),就可能污染這些物件。對用戶輸入進行清理的一個簡單方法是,使用規則運算式來處理它。在這個示例中,只希望接受字母。將字串限制為特定數量的字元,或者要求所有字母都是小寫的,這可能也是個好主意。
清單 3. 使用戶輸入變得安全   
$myUsername = cleanInput($_POST['username']); //clean!   
$arrayarrayUsers = array($myUsername, ‘tom’, ‘tommy’); //clean!   
define(“GREETING”, ‘hello there’ . $myUsername); //clean!   
function cleanInput($input){   $clean = strtolower($input);   
$clean = preg_replace(“/[^a-z]/”, “”, $clean);   
$clean = substr($clean,0,12);return $clean;   
}   
?>  
  
規則 2:禁用那些使安全性難以實施的PHP設置
  已經知道了不能信任用戶輸入,還應該知道不應該信任機器上配置PHP 的方式。例如,要確保禁用 register_globals。如果啟用了register_globals,就可能做一些粗心的事情,比如使用 $variable 替換同名的 GET  POST 字串。通過禁用這個設置,PHP 強迫您在正確的名稱空間中引用正確的變數。要使用來自表單 POST 的變數,應該引用$_POST['variable']。這樣就不會將這個特定變數誤會成 cookie、會話或GET 變數。
  
規則 3:如果不能理解它,就不能保護它
  一些開發人員使用奇怪的語法,或者將語句組織得很緊湊,形成簡短但是含義模糊的代碼。這種方式可能效率高,但是如果您不理解代碼正在做什麼,那麼就無法決定如何保護它。例如,您喜歡下面兩段代碼中的哪一段?
清單 4. 使代碼容易得到保護   
//obfuscated code   
$input = (isset($_POST['username']) ? $_POST['username']:”);   
//unobfuscated code   
$input = ”;   
if (isset($_POST['username'])){   
$input = $_POST['username'];   
}else{   
$input = ”;   
}  
  在第二個比較清晰的程式碼片段中,很容易看出 $input 是有瑕疵的,需要進行清理,然後才能安全地處理。
  
規則 4縱深防禦” 是新的法寶
  本教程將用示例來說明如何保護線上表單,同時在處理表單的 PHP代碼中採用必要的措施。同樣,即使使用 PHP regex 來確保 GET 變數完全是數位的,仍然可以採取措施確保 SQL 查詢使用轉義的用戶輸入。縱深防禦不只是一種好思想,它可以確保您不會陷入嚴重的麻煩。既然已經討論了基本規則,現在就來研究第一種威脅:SQL 注入攻擊。
  防止SQL注入攻擊
  在SQL注入攻擊中,用戶通過操縱表單或 GET 查詢字串,將資訊添加到資料庫查詢中。例如,假設有一個簡單的登錄資料庫。這個資料庫中的每個記錄都有一個用戶名欄位和一個密碼欄位。構建一個登錄表單,讓用戶能夠登錄。
<html>  
<head>  
<title>Login</title>  
</head>  
<body>  
<form action=”verify.php” method=”post”>  
<p><label for=’user’>Username</label>  
<input type=’text’ name=’user’ id=’user’/>  
</p> <p><label for=’pw’>Password</label>  
<input type=’password’ name=’pw’ id=’pw’/>  
</p> <p><input type=’submit’ value=’login’/></p>  
</form>  
</body>  
</html>  
  這個表單接受使用者輸入的用戶名和密碼,並將使用者輸入提交給名為verify.php的文件。在這個檔中,PHP處理來自登錄表單的資料,如下所示:
清單 5. 不安全的 PHP 表單處理代碼   
<?php  
$okay = 0;   
$username = $_POST['user'];   
$pw = $_POST['pw'];   
$sql = “select count(*) as ctr from users where username=’
”.$username.”‘ and password=’”. $pw.”‘ limit 1;   
$result = MySQL_query($sql);   
while ($data = mysql_fetch_object($result)){   
if ($data->ctr == 1){   
//they’re okay to enter The application!   
$okay = 1;   
}   
}   
if ($okay){   
$_SESSION['loginokay'] = true;   
header(“index.php”);   
}else{   
header(“login.php”);   
}   
?>  
  這段代碼看起來沒問題,對嗎?世界各地成百(甚至成千)PHP/MySQL 網站都在使用這樣的代碼。它錯在哪裡?好,記住 “不能信任用戶輸入。這裡沒有對來自使用者的任何資訊進行轉義,因此使應用程式容易受到攻擊。具體來說,可能會出現任何類型的SQL注入攻擊。例如,如果用戶輸入 foo 作為用戶名,輸入  or 1=作為密碼,那麼實際上會將以下字串傳遞給 PHP,然後將查詢傳遞給 MySQL
<?php  
$sql = “select count(*) as ctr from users where username=
’foo’ and password=” or ’1=’1 limit 1;   
?>  
  這個查詢總是返回計數值 1,因此 PHP 會允許進行訪問。通過在密碼字串的末尾注入某些惡意 SQL,駭客就能裝扮成合法的用戶。解決這個問題的辦法是,將 PHP 的內置 mysql_real_escape_string() 函數用作任何使用者輸入的包裝器。這個函數對字串中的字元進行轉義,使字串不可能傳遞撇號等特殊字元並讓 MySQL 根據特殊字元進行操作。清單7展示了帶轉義處理的代碼。
清單7展示了帶轉義處理的代碼   
<?php     
$okay = 0;     
$username = $_POST['user'];     
$pw = $_POST['pw'];     
$sql = ”select count(*) as ctr from users where username=’”.mysql_real_
_string($username).”‘ and password=’”. mysql_real_escape_string($pw).”‘
 limit 1;      
$result = mysql_query($sql);     
while ($data = mysql_fetch_object($result)){         
       if ($data->ctr == 1){          //they’re okay to enter the 
application!             
       $okay = 1;          
       }     
      }     
       if ($okay){         
           $_SESSION['loginokay'] = true;         
           header(“index.php”);     
           }   
        else{         
        header(“login.php”);     
      }     
?>  
  使用 mysql_real_escape_string() 作為用戶輸入的包裝器,就可以避免用戶輸入中的任何惡意 SQL 注入。如果使用者嘗試通過 SQL 注入傳遞畸形的密碼,那麼會將以下查詢傳遞給資料庫:
select count(*) as ctr from users where username=’foo’ and password=
’’ or ’1’=’1 limit 1 

自由轉載,轉載請注明: 轉載自WEB開發筆記 www.chhua.com

本文來源於WEB開發筆記 http://www.chhua.com , 原文地址:http://www.chhua.com/web-note1413 

1 則留言 :

  1. 生產力 軟體 無線網路 硬體 程式 網頁設計 網路 ajax Algorithm Android Blogger chrome codeigniter css Eclipse Facebook git Google html Java JavaScript jQuery json Life Tips php programming python ruby security social network SQL tonyq

    回覆刪除