安全

以CGI模式安裝

CGI、FastCGI模式
CGI是通用網(wǎng)關(guān)(Common Gateway Interface)接口,簡單說就是連接Web服務(wù)器中的執(zhí)行程序和網(wǎng)頁資源的一座橋,它把網(wǎng)頁接收的請求交給服務(wù)器的執(zhí)行程序,再把服務(wù)器執(zhí)行程序的結(jié)果返還給網(wǎng)頁。每個(gè)請求到來時(shí),都會(huì)先創(chuàng)建一個(gè)CGI的子進(jìn)程,處理請求,結(jié)束子進(jìn)程。以執(zhí)行php請求為例,CGI會(huì)重新解析php.ini、重新載入全部dll擴(kuò)展并重初始化全部數(shù)據(jù)結(jié)構(gòu),解釋執(zhí)行php腳本,如果請求的數(shù)量大時(shí),頻繁的反復(fù)加載CGI子進(jìn)程會(huì)大量擠占系統(tǒng)資源和內(nèi)存,導(dǎo)致性能低下。這時(shí)候就有了CGI的升級版本:FastCGI模式,它像一個(gè)常駐型的CGI,只要啟動(dòng)后,就會(huì)有固定數(shù)量的CGI進(jìn)程在那里等待接收請求并執(zhí)行,執(zhí)行完請求之后并不結(jié)束進(jìn)程,而是把這個(gè)進(jìn)程放回可用的進(jìn)程池里面繼續(xù)等待新的請求。CGI的進(jìn)程有動(dòng)態(tài)伸縮性質(zhì),它啟動(dòng)時(shí)自動(dòng)fork一個(gè)進(jìn)程池,會(huì)選出一個(gè)空閑的進(jìn)程執(zhí)行新的請求,當(dāng)進(jìn)程池沒有空閑進(jìn)程時(shí),會(huì)fork出新的子進(jìn)程執(zhí)行,但是在php-fpm.conf里面有配置cgi進(jìn)程數(shù)量的參數(shù),設(shè)置了最大進(jìn)程數(shù)和最小進(jìn)程數(shù),當(dāng)請求數(shù)量超過最大進(jìn)程數(shù)時(shí),php就不能執(zhí)行新的請求了。

CGI模式安裝可能會(huì)受到的攻擊
CGI模式安裝通常會(huì)把php的可執(zhí)行文件安裝到web服務(wù)器的cgi-bin目錄,這樣在訪問系統(tǒng)文件時(shí)可以把cgi-bin目錄添加在url里面,栗子:http://my.host/cgi-bin/php?/etc/passwd ?后面的參數(shù)表示php的命令行參數(shù),請求這個(gè)URL,web服務(wù)器會(huì)執(zhí)行 php /etc/passwd 命令,這樣一來,我想要運(yùn)行服務(wù)器上的那個(gè)腳本,只需要知道腳本所在的位置并把它加在url里面,就可以任意執(zhí)行了,這樣是很不安全的。下面介紹幾種方式可以減少這種攻擊的傷害。

1、權(quán)限控制

在每個(gè)腳本所在的目錄都設(shè)置一個(gè)檢查權(quán)限的腳本,如果有權(quán)限訪問,再執(zhí)行腳本。栗子:訪問http://my.host/cgi-bin/php/secret/doc.html 想要訪問/secret/doc.html,創(chuàng)建http://my.host/cgi-bin/php/secret/check.php的重定向,先從這個(gè)腳本檢查權(quán)限,通過驗(yàn)證再去執(zhí)行腳本。

2、使用 --enable-force-cgi-redirect 選項(xiàng)

在編譯PHP的configure腳本中指定參數(shù): --enable-force-cgi-redirect,可以防止任何人通過http://my.host/cgi-bin/php/secretdir/script.php這樣的URL直接調(diào)用php,HP 在此模式下只會(huì)解析已經(jīng)通過了 web 服務(wù)器的重定向規(guī)則的 URL。

3、設(shè)置 doc_root 或 user_dir

配置文件的 doc_root選項(xiàng) 或設(shè)置環(huán)境變量 PHP_DOCUMENT_ROOT可以定義php 腳本執(zhí)行目錄,如果設(shè)置了該項(xiàng),那么php就只會(huì)解釋 doc_root 目錄下的文并且目錄外的腳本不會(huì)被php解釋器執(zhí)行,但是user_dir除外。另外一個(gè)user_dir選項(xiàng),當(dāng)這個(gè)選項(xiàng)沒有被設(shè)置時(shí),doc_root是唯一決定哪里的腳本能被執(zhí)行的選項(xiàng)。設(shè)置了這個(gè)選項(xiàng),就會(huì)執(zhí)行用戶主目錄下的user_dir子目錄下的文件,栗子:設(shè)置user_dir=public_php,那么訪問http://my.host/~user/doc.php,就會(huì)訪問用戶主目錄下的public_php子目錄下的doc.php文件。如果用戶的主目錄是/home/user,那么將會(huì)執(zhí)行文件/home/user/public_php/doc.php。

4、php解釋器放在 web 目錄以外

一個(gè)非常安全的做法就是把php解釋器放在 web 目錄外的地方,比如說 /usr/local/bin。這樣做唯一不便的地方就是必須在每一個(gè)包含 PHP 代碼的文件的第一行加上#!/usr/local/bin/php,還要將這些文件的屬性改成可執(zhí)行。

以apache模塊安裝

當(dāng) PHP 以 Apache 模塊方式安裝時(shí),它將繼承 Apache 用戶(通常為“nobody”)的權(quán)限。就是說,如果用php操作數(shù)據(jù)庫,而數(shù)據(jù)庫剛好沒有做訪問控制,apache用戶是nobody,那么php也是用nobody訪問數(shù)據(jù)庫,一些惡意的腳本即使不提供用戶名和密碼就可以操作數(shù)據(jù)庫了。

文件系統(tǒng)安全

如果我們的web服務(wù)提供這樣一個(gè)功能:用戶可以刪除服務(wù)器上某些文件,只需要在表單中提交要?jiǎng)h除的文件即可。下面是代碼實(shí)現(xiàn):
<pre>
$username = $_POST['user_submitted_name’];//要?jiǎng)h除的文件目錄
$userfile = $_POST['user_submitted_filename’];//要?jiǎng)h除的文件名
$homedir = "/home/$username";
unlink ("$homedir/$userfile");
echo "The file has been deleted!";
</pre>
如果用戶提交的文件是/etc/passwd,而web服務(wù)器有權(quán)限可以刪除這個(gè)文件,那么重要的文件就這么被刪除了。可以用2個(gè)措施來防止這類問題:1、只給web用戶很小的權(quán)限 2、檢查所有提交上來的變量,下面是改進(jìn)的腳本:
<pre>
$username = $_SERVER['REMOTE_USER']; // 使用認(rèn)證機(jī)制
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username”;
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
//檢查用戶要?jiǎng)h除的是不是他自己目錄下的文件
$logstring = "Deleted $filepath\n";
} else {
$logstring = "Failed to delete $filepath\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite ($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
</pre>
但是,如果用戶用../etc/作為自己的用戶名呢?這個(gè)時(shí)候還需要對文件名進(jìn)行檢查,檢查是否是字母、數(shù)字、下劃線的組合。

數(shù)據(jù)庫安全

數(shù)據(jù)庫已經(jīng)成為各個(gè)動(dòng)態(tài)網(wǎng)站上的重要組成部分,各種用戶隱私、敏感數(shù)據(jù)等都保存在數(shù)據(jù)庫中,所以數(shù)據(jù)庫安全尤為重要。下面介紹一下我們在使用數(shù)據(jù)庫時(shí)應(yīng)該注意哪些安全方面的問題。

設(shè)計(jì)數(shù)據(jù)庫

第一步一般都是創(chuàng)建數(shù)據(jù)庫,當(dāng)創(chuàng)建一個(gè)數(shù)據(jù)庫的時(shí)候,會(huì)指定一個(gè)所有者來執(zhí)行新建數(shù)據(jù)庫的sql語句,只有所有者或超級用戶才有權(quán)任意操作數(shù)據(jù)庫,如果想讓其他用戶使用,必須授權(quán)。應(yīng)用程序永遠(yuǎn)不要使用數(shù)據(jù)庫所有者或超級用戶帳號來連接數(shù)據(jù)庫,因?yàn)檫@些帳號可以執(zhí)行任意的操作,比如說修改數(shù)據(jù)庫結(jié)構(gòu)(例如刪除一個(gè)表)或者清空整個(gè)數(shù)據(jù)庫的內(nèi)容。應(yīng)該為應(yīng)用程序的不同功能創(chuàng)建不同的數(shù)據(jù)庫賬號,并且只賦予他們功能所需要的對數(shù)據(jù)庫對象操作的權(quán)限,避免同一個(gè)用戶可以完成另一個(gè)用戶的事。最好不要把所有的事務(wù)邏輯都用腳本實(shí)現(xiàn),最好用視圖(view)、觸發(fā)器(trigger)或者規(guī)則(rule)在數(shù)據(jù)庫層面完成。

連接數(shù)據(jù)庫

把連接建立在 SSL 加密技術(shù)上可以增加客戶端和服務(wù)器端通信的安全性,或者 SSH 也可以用于加密客戶端和數(shù)據(jù)庫之間的連接。

加密存儲模型

ssh/ssl只能加密客戶端和服務(wù)端交換的數(shù)據(jù),并不難保護(hù)數(shù)據(jù)庫中已有的數(shù)據(jù)。所以創(chuàng)建自己的加密機(jī)制,在保存數(shù)據(jù)庫之前,先把數(shù)據(jù)加密然后再存儲,讀數(shù)據(jù)時(shí)再解密。

SQL注入

最終執(zhí)行的sql語句中一般都包含用戶提交的數(shù)據(jù),如果不對用戶提交的數(shù)據(jù)進(jìn)行驗(yàn)證的話會(huì)有很大的安全漏洞,栗子:一段實(shí)現(xiàn)數(shù)據(jù)分頁顯示的代碼,也可以被用作創(chuàng)建一個(gè)超級用戶(PostgreSQL系統(tǒng))。
<pre>
$offset = $argv[0]; // 注意,沒有輸入驗(yàn)證!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);
</pre>
一般的用戶只會(huì)點(diǎn)擊$offset被賦值的“上一頁”、“下一頁”的鏈接,這樣$offset只是一個(gè)數(shù)值,但是如果有人把下面的語句先經(jīng)過urlencode()處理,再加入url中的話,那么他就可以創(chuàng)建一個(gè)超級用戶了,一開始的”0”只是為了提供一個(gè)正確的偏移量以便補(bǔ)充完整原來的查詢,”—“是sql中的注釋,表示告訴sql解釋器忽略后面的語句。
<pre>
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select 'crack', usesysid, 't','t','crack'
from pg_shadow where usename='postgres';
</pre>

預(yù)防sql注入的方法

1、不要使用超級用戶或所有者賬號去連接數(shù)據(jù)庫,要用權(quán)限被嚴(yán)格控制的賬號
2、檢查輸入的數(shù)據(jù)格式是否正確
3、用settype()函數(shù)來轉(zhuǎn)換數(shù)據(jù)類型,也可以用sprintf()把它格式化為數(shù)字。
4、使用數(shù)據(jù)庫特定的敏感字符轉(zhuǎn)義函數(shù)(比如 mysql_escape_string() 和 sql_escape_string())把用戶提交上來的非數(shù)字?jǐn)?shù)據(jù)進(jìn)行轉(zhuǎn)義。如果數(shù)據(jù)庫沒有專門的敏感字符轉(zhuǎn)義功能的話 addslashes() 和 str_replace() 可以代替完成這個(gè)工作。
5、也可以選擇使用數(shù)據(jù)庫的存儲過程和預(yù)定義指針等特性來抽象數(shù)庫訪問,使用戶不能直接訪問數(shù)據(jù)表和視圖。但這個(gè)辦法又有別的影響。
6、保存數(shù)據(jù)庫的查詢?nèi)罩?,雖然不能防止任何攻擊,但利用日志可以跟蹤到哪個(gè)程序曾經(jīng)被嘗試攻擊過,方便分析問題。

錯(cuò)誤報(bào)告

通常 PHP 所返回的錯(cuò)誤提示都能幫助開發(fā)者調(diào)試程序,它會(huì)提出哪個(gè)文件的哪些函數(shù)或代碼出錯(cuò),并指出錯(cuò)誤發(fā)生的在文件的第幾行,這些就是 PHP 本身所能給出的信息,但是攻擊者故意輸入錯(cuò)誤的數(shù)據(jù),然后查看錯(cuò)誤提示的類型和上下文,這樣就可以收集服務(wù)器的信息以便尋找弱點(diǎn)。解決辦法是在代碼中利用 error_reporting() 函數(shù)來定位問題,使代碼更安全。在發(fā)布程序之前,先打開 E_ALL 測試代碼,可以幫你很快找到變量使用不當(dāng)?shù)牡胤?。一旦?zhǔn)備正式發(fā)布,就應(yīng)該把 error_reporting() 的參數(shù)設(shè)為 0 來徹底關(guān)閉錯(cuò)誤報(bào)告或者把 php.ini 中的 display_errors 設(shè)為 off 來關(guān)閉所有的錯(cuò)誤顯示使代碼不能被探測。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容