五、文件上傳
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