PHP安全配置 上傳文件(轉)

五、文件上傳 

php的文件上傳機制是把用戶上傳的文件保存在php.ini的upload_tmp_dir定義的臨時目錄(默認是系統的臨時目錄,如:/tmp)裏的一個相似phpxXuoXG的隨機臨時文件,程序執行結束,該臨時文件也被刪除。PHP給上傳的文件定義了四個變量:(如form變量名是file,並且register_globals打開) 

$file #就是保存到服務器端的臨時文件(如/tmp/phpxXuoXG ) 
$file_size #上傳文件的大小 
$file_name #上傳文件的原始名稱 
$file_type #上傳文件的類型 



推薦使用: 

$HTTP_POST_FILES['file']['tmp_name'] 
$HTTP_POST_FILES['file']['size'] 
$HTTP_POST_FILES['file']['name'] 
$HTTP_POST_FILES['file']['type'] 



這是一個最簡單的文件上傳代碼: 

//test_5.php 
if(isset($upload) && $file != "none") { 
copy($file, "/usr/local/apache/htdocs/upload/".$file_name); 
echo "文件".$file_name."上傳成功!點擊繼續上傳"; 
exit; 
?> 



content="text/html; charset=gb2312"> 




上傳文件: 
 
 







這樣的上傳代碼存在讀取任意文件和執行命令的重大問題。 

下面的請求能夠把/etc/passwd文檔拷貝到web目錄/usr/local/apache/htdocs/test(注意:這個目錄必須nobody可寫)下的attack.txt文件裏: 

http://victim/test_5.php?upload=1&file=/etc/passwd&file_name=attack.txt 

而後能夠用以下請求讀取口令文件: 

http://victim/test/attack.txt 

攻擊者能夠把php文件拷貝成其它擴展名,泄漏腳本源代碼。 

攻擊者能夠自定義form裏file_name變量的值,上傳覆蓋任意有寫權限的文件。 

攻擊者還能夠上傳PHP腳本執行主機的命令。 

解決方法: 

PHP-4.0.3之後提供了is_uploaded_file和move_uploaded_file函數,能夠檢查操做的文件是不是用戶上傳的文件,從而避免把系統文件拷貝到web目錄。 

使用$HTTP_POST_FILES數組來讀取用戶上傳的文件變量。 

嚴格檢查上傳變量。好比不容許是php腳本文件。 

把PHP腳本操做限制在web目錄能夠避免程序員使用copy函數把系統文件拷貝到web目錄。move_uploaded_file不受open_basedir的限制,因此沒必要修改php.ini裏upload_tmp_dir的值。 

把PHP腳本用phpencode進行加密,避免因爲copy操做泄漏源碼。 

嚴格配置文件和目錄的權限,只容許上傳的目錄可以讓nobody用戶可寫。 

對於上傳目錄去掉PHP解釋功能,能夠經過修改httpd.conf實現: 


php_flag engine off 
#若是是php3換成php3_engine off 




重啓apache,upload目錄的php文件就不能被apache解釋了,即便上傳了php文件也沒有問題,只能直接顯示源碼。 

六、命令執行 

下面的代碼片段是從PHPNetToolpack摘出,詳細的描述見: 

http://www.securityfocus.com/bid/4303 

//test_6.php 
system("traceroute $a_query",$ret_strs); 
?> 



因爲程序沒有過濾$a_query變量,因此攻擊者能夠用分號來追加執行命令。 

攻擊者輸入以下請求能夠執行cat /etc/passwd命令: 

http://victim/test_6.php?a_query=www.example.com;cat /etc/passwd 

PHP的命令執行函數還有system(), passthru(), popen()和``等。命令執行函數很是危險,慎用。若是要使用必定要嚴格檢查用戶輸入。 

解決方法: 

要求程序員使用escapeshellcmd()函數過濾用戶輸入的shell命令。 

啓用safe_mode能夠杜絕不少執行命令的問題,不過要注意PHP的版本必定要是最新的,小於PHP-4.2.2的均可能繞過safe_mode的限制去執行命令。 

七、sql_inject 

以下的SQL語句若是未對變量進行處理就會存在問題: 

select * from login where user='$user' and pass='$pass' 



攻擊者能夠用戶名和口令都輸入1' or 1='1繞過驗證。 

不過幸好PHP有一個默認的選項magic_quotes_gpc = On,該選項使得從GET, POST, COOKIE來的變量自動加了addslashes()操做。上面SQL語句變成了: 

select * from login where user='1\' or 
1=\'1' and pass='1\' or 1=\'1' 



從而避免了此類sql_inject攻擊。 

對於數字類型的字段,不少程序員會這樣寫: 

select * from test where id=$id 



因爲變量沒有用單引號擴起來,就會形成sql_inject攻擊。幸好MySQL功能簡單,沒有sqlserver等數據庫有執行命令的SQL語句,並且PHP的mysql_query()函數也只容許執行一條SQL語句,因此用分號隔開多條SQL語句的攻擊也不能奏效。可是攻擊者起碼還可讓查詢語句出錯,泄漏系統的一些信息,或者一些意想不到的狀況。 

解決方法: 

要求程序員對全部用戶提交的要放到SQL語句的變量進行過濾。 

即便是數字類型的字段,變量也要用單引號擴起來,MySQL本身會把字串處理成數字。 

在MySQL裏不要給PHP程序高級別權限的用戶,只容許對本身的庫進行操做,這也避免了程序出現問題被 SELECT INTO OUTFILE ... 這種攻擊。 

八、警告及錯誤信息 

PHP默認顯示全部的警告及錯誤信息: 

error_reporting = E_ALL & ~E_NOTICE 
display_errors = On 



在平時開發調試時這很是有用,能夠根據警告信息立刻找到程序錯誤所在。 

正式應用時,警告及錯誤信息讓用戶不知所措,並且給攻擊者泄漏了腳本所在的物理路徑,爲攻擊者的進一步攻擊提供了有利的信息。並且因爲本身沒有訪問到錯誤的地方,反而不能及時修改程序的錯誤。因此把PHP的全部警告及錯誤信息記錄到一個日誌文件是很是明智的,即不給攻擊者泄漏物理路徑,又能讓本身知道程序錯誤所在。 

修改php.ini中關於Error handling and logging部份內容: 

error_reporting = E_ALL 
display_errors = Off 
log_errors = On 
error_log = /usr/local/apache/logs/php_error.log 



而後重啓apache,注意文件/usr/local/apache/logs/php_error.log必需可讓nobody用戶可寫。
本文來源:http://linux.sheup.com/linux/linux4945.htm
相關文章
相關標籤/搜索