淺談php安全

這段時間一直在寫一個整站柒傻,前幾天才基本完成了,所以抽個時間寫了一篇對于php安全的總結(jié)较木。技術(shù)含量不高红符,過不了也沒關(guān)系,希望能一些準(zhǔn)備寫網(wǎng)站的朋友一點引導(dǎo)伐债。

在放假之初预侯,我抽時間看了《白帽子講web安全》,吳翰清基本上把web安全中所有能夠遇到的問題峰锁、解決思路歸納總結(jié)得很清晰萎馅,也是我這一次整體代碼安全性的基石。

我希望能分如下幾個方面來分享自己的經(jīng)驗

把握整站的結(jié)構(gòu)虹蒋,避免泄露站點敏感目錄

在寫代碼之初糜芳,我也是像很多老源碼一樣飒货,在根目錄下放上index.php、register.php峭竣、login.php塘辅,用戶點擊注冊頁面,就跳轉(zhuǎn)到http://localhost/register.php皆撩。并沒有太多的結(jié)構(gòu)的思想扣墩,像這樣的代碼結(jié)構(gòu),最大的問題倒不是安全性問題扛吞,而是代碼擴(kuò)展與移植問題呻惕。

在寫代碼的過程中,我們常要對代碼進(jìn)行修改喻粹,這時候如果代碼沒有統(tǒng)一的一個入口點蟆融,我們可能要改很多地方。后來我讀了一點emlog的代碼守呜,發(fā)現(xiàn)網(wǎng)站真正的前端代碼都在模板目錄里型酥,而根目錄下就只有入口點文件和配置文件。這才頓悟查乒,對整個網(wǎng)站的結(jié)構(gòu)進(jìn)行了修改弥喉。

網(wǎng)站根目錄下放上一個入口點文件,讓它來對整個網(wǎng)站所有頁面進(jìn)行管理玛迄,這個時候注冊頁面變成了http://localhost/?act=register由境,任何頁面只是act的一個參數(shù),在得到這個參數(shù)后蓖议,再用一個switch來選擇要包含的文件內(nèi)容虏杰。在這個入口點文件中,還可以包含一些常量的定義勒虾,比如網(wǎng)站的絕對路徑纺阔、網(wǎng)站的地址、數(shù)據(jù)庫用戶密碼修然。以后我們在腳本的編寫中笛钝,盡量使用絕對路徑而不要使用相對路徑(否則腳本如果改變位置,代碼也要變)愕宋,而這個絕對路徑就來自入口點文件中的定義玻靡。

當(dāng)然,在安全性上中贝,一個入口點文件也能隱藏后臺地址囤捻。像這樣的地址http://localhost/?act=xxx不會暴露后臺絕對路徑,甚至可以經(jīng)常更改雄妥,不用改變太多代碼最蕾。一個入口點文件也可以驗證訪問者的身份依溯,比如一個網(wǎng)站后臺,不是管理員就不允許查看任何頁面瘟则。在入口點文件中就可以驗證身份黎炉,如果沒有登錄,就輸出404頁面醋拧。

有了入口點文件慷嗜,我就把所有非入口點文件前面加上了這句話:

php?if(!defined('WWW_ROOT'))?{header("HTTP/1.1?404?Not?Found");?exit;}??>

WWW_ROOT是我在入口點中定義的一個常量,如果用戶是通過這個頁面的絕對路徑訪問(http://localhost/register.php)丹壕,我就輸出404錯誤庆械;只有通過入口點訪問(http://localhost/?act=register),才能執(zhí)行后面的代碼菌赖。

使用預(yù)編譯語句缭乘,避免sql注入

注入是早前很大的一個問題,不過近些年因為大家比較重視這個問題琉用,所以慢慢變得好了很多堕绩。

吳翰清在web白帽子里說的很好,其實很多漏洞邑时,像sql注入或xss奴紧,都是將“數(shù)據(jù)”和“代碼”沒有區(qū)分開【穑“代碼”是程序員寫的內(nèi)容黍氮,“數(shù)據(jù)”是用戶可以改變的內(nèi)容。如果我們寫一個sql語句select

* from admin where username='admin' password='xxxxx',

admin和xxxxx就是數(shù)據(jù)浅浮,是用戶輸入的用戶名和密碼沫浆,但如果沒有任何處理,用戶輸入的就可能是“代碼”滚秩,比如'or

''='件缸,這樣就造成了漏洞∈逅欤“代碼”是絕對不能讓用戶接觸的。

在php中争剿,對于mysql數(shù)據(jù)庫有兩個模塊已艰,mysql和mysqli,mysqli的意思就是mysql

improve蚕苇。mysql的改進(jìn)版哩掺,這個模塊中就含有“預(yù)編譯”這個概念。像上面那個sql語句涩笤,改一改:select * from admin

where username='?'

password='?'嚼吞,它就不是一個sql語句了盒件,但是可以通過mysqli的預(yù)編譯功能先把他編譯成stmt對象,在后期用戶輸入賬號密碼后舱禽,用stmt->bind_param將用戶輸入的“數(shù)據(jù)”綁定到這兩個問號的位置炒刁。這樣,用戶輸入的內(nèi)容就只能是“數(shù)據(jù)”誊稚,而不可能變成“代碼”翔始。

這兩個問號限定了“數(shù)據(jù)”的位置,以及sql語句的結(jié)構(gòu)里伯。我們可以把我們所有的數(shù)據(jù)庫操作都封裝到一個類中城瞎,所有sql語句的執(zhí)行都進(jìn)行預(yù)編譯。這樣就完全避免了sql注入疾瓮,這也是吳翰清最推薦的解決方案脖镀。

下面是使用mysqli的一些代碼部分(所有的判斷函數(shù)運行成功或失敗的代碼我都省略了,但不代表不重要):

php

//用戶輸入的數(shù)據(jù)

$name?=?'admin';

$pass?=?'123456';

//首先新建mysqli對象,構(gòu)造函數(shù)參數(shù)中包含了數(shù)據(jù)庫相關(guān)內(nèi)容狼电。

$conn?=?new?mysqli(DB_HOST,?DB_USER,?DB_PASS,?DB_NAME,?DB_PORT);

//設(shè)置sql語句默認(rèn)編碼

$this->mysqli->set_charset("utf8");

//創(chuàng)建一個使用通配符的sql語句

$sql?=?'SELECT?user_id?FROM?admin?WHERE?username=??AND?password=?;';

//編譯該語句蜒灰,得到一個stmt對象.

$stmt?=?$conn->prepare($sql);

/********************之后的內(nèi)容就能重復(fù)利用,不用再次編譯*************************/

//用bind_param方法綁定數(shù)據(jù)

//大家可以看出來漫萄,因為我留了兩個?卷员,也就是要向其中綁定兩個數(shù)據(jù),所以第一個參數(shù)是綁定的數(shù)據(jù)的類型(s=string,i=integer)腾务,第二個以后的參數(shù)是要綁定的數(shù)據(jù)

$stmt->bind_param('ss',?$name,?$pass);

//調(diào)用bind_param方法綁定結(jié)果(如果只是檢查該用戶與密碼是否存在毕骡,或只是一個DML語句的時候,不用綁定結(jié)果)

//這個結(jié)果就是我select到的字段岩瘦,有幾個就要綁定幾個

$stmt->bind_result($user_id);

//執(zhí)行該語句

$stmt->execute();

//得到結(jié)果

if($stmt->fetch()){

echo?'登陸成功';

//一定要注意釋放結(jié)果資源未巫,否則后面會出錯

$stmt->free_result();

return?$user_id;?//返回剛才select到的內(nèi)容

}else{echo?'登錄失敗';}

?>

預(yù)防XSS代碼,如果不需要使用cookie就不使用

在我的網(wǎng)站中并沒有使用cookie启昧,更因為我對權(quán)限限制的很死叙凡,所以對于xss來說危險性比較小。

對于xss的防御密末,也是一個道理握爷,處理好“代碼”和“數(shù)據(jù)”的關(guān)系。當(dāng)然严里,這里的代碼指的就是javascript代碼或html代碼新啼。用戶能控制的內(nèi)容,我們一定要使用htmlspecialchars等函數(shù)來處理用戶輸入的數(shù)據(jù)刹碾,并且在javascript中要謹(jǐn)慎把內(nèi)容輸出到頁面中燥撞。

限制用戶權(quán)限,預(yù)防CSRF

現(xiàn)在腳本漏洞比較火的就是越權(quán)行為,很多重要操作使用GET方式執(zhí)行物舒,或使用POST方式執(zhí)行而沒有核實執(zhí)行者是否知情色洞。

CSRF很多同學(xué)可能比較陌生,其實舉一個小例子就行了:

A冠胯、B都是某論壇用戶火诸,該論壇允許用戶“贊”某篇文章,用戶點“贊”其實是訪問了這個頁面:http://localhost/?act=support&articleid=12涵叮。這個時候惭蹂,B如果把這個URL發(fā)送給A,A在不知情的情況下打開了它割粮,等于說給articleid=12的文章贊了一次盾碗。

所以該論壇換了種方式,通過POST方式來贊某篇文章舀瓢。

?action="http://localhost/?act=support"?method="POST">

?type="hidden"?value="12"?name="articleid">

?type="submit"?value="贊">

可以看到一個隱藏的input框里含有該文章的ID廷雅,這樣就不能通過一個URL讓A點擊了。但是B可以做一個“極具誘惑力”的頁面京髓,其中某個按鈕就寫成這樣一個表單航缀,來誘惑A點擊。A一點擊堰怨,依舊還是贊了這篇文章芥玉。

最后,該論壇只好把表單中增加了一個驗證碼备图。只有A輸入驗證碼才能點贊灿巧。這樣,徹底死了B的心揽涮。

但是抠藕,你見過哪個論壇點“贊”也要輸入驗證碼?

所以吳翰清在白帽子里也推薦了最好的方式蒋困,就是在表單中加入一個隨機(jī)字符串token(由php生成盾似,并保存在SESSION中),如果用戶提交的這個隨機(jī)字符串和SESSION中保存的字符串一致雪标,才能贊零院。

在B不知道A的隨機(jī)字符串時,就不能越權(quán)操作了村刨。

我在網(wǎng)站中也多次使用了TOKEN门粪,不管是GET方式還是POST方式,通常就能抵御99%的CSRF估計了烹困。

嚴(yán)格控制上傳文件類型

上傳漏洞是很致命的漏洞,只要存在任意文件上傳漏洞乾吻,就能執(zhí)行任意代碼髓梅,拿到webshell拟蜻。

我在上傳這部分,寫了一個php類枯饿,通過白名單驗證酝锅,來控制用戶上傳惡意文件。在客戶端奢方,我通過javascript先驗證了用戶選擇的文件的類型搔扁,但這只是善意地提醒用戶,最終驗證部分蟋字,還是在服務(wù)端稿蹲。

白名單是必要的,你如果只允許上傳圖片鹊奖,就設(shè)置成array('jpg','gif','png','bmp')苛聘,當(dāng)用戶上傳來文件后,取它的文件名的后綴忠聚,用in_array驗證是否在白名單中设哗。

在上傳文件數(shù)組中,會有一個MIME類型两蟀,告訴服務(wù)端上傳的文件類型是什么网梢,但是它是不可靠的,是可以被修改的赂毯。在很多存在上傳漏洞的網(wǎng)站中战虏,都是只驗證了MIME類型,而沒有取文件名的后綴驗證欢瞪,導(dǎo)致上傳任意文件活烙。

所以我們在類中完全可以忽略這個MIME類型,而只取文件名的后綴遣鼓,如果在白名單中啸盏,才允許上傳。

當(dāng)然骑祟,服務(wù)器的解析漏洞也是很多上傳漏洞的突破點回懦,所以我們盡量把上傳的文件重命名,以“日期時間+隨機(jī)數(shù)+白名單中后綴”的方式對上傳的文件進(jìn)行重命名次企,避免因為解析漏洞而造成任意代碼執(zhí)行怯晕。

加密混淆javascript代碼,提高攻擊門檻

很多xss漏洞缸棵,都是黑客通過閱讀javascript代碼發(fā)現(xiàn)的舟茶,如果我們能把所有javascript代碼混淆以及加密,讓代碼就算解密后也是混亂的(比如把所有變量名替換成其MD5 hash值),提高閱讀的難度吧凉。

使用更高級的hash算法保存數(shù)據(jù)庫中重要信息

在這個硬盤容量大增的時期隧出,很多人擁有很大的彩虹表,再加上類似于cmd5這樣的網(wǎng)站的大行其道阀捅,單純的md5已經(jīng)等同于無物胀瞪,所以我們迫切的需要更高級的hash算法,來保存我們數(shù)據(jù)庫中的密碼饲鄙。

所以后來出現(xiàn)了加salt的md5凄诞,比如discuz的密碼就是加了salt。其實salt就是一個密碼的“附加值”忍级,比如A的密碼是123456帆谍,而我們設(shè)置的salt是abc,這樣保存到數(shù)據(jù)庫的可能就是md5('123456abc'),增加了破解的難度颤练。

但是黑客只要得知了該用戶的salt也能跑md5跑出來既忆。因為現(xiàn)在的計算機(jī)的計算速度已經(jīng)非常快了嗦玖,一秒可以計算10億次md5值患雇,弱一點的密碼分把鐘就能跑出來。

所以后來密碼學(xué)上改進(jìn)了hash宇挫,引進(jìn)了一個概念:密鑰延伸苛吱。說簡單點就是增加計算hash的難度(比如把密碼用md5()函數(shù)循環(huán)計算1000次),故意減慢計算hash所用的時間器瘪,以前一秒可以計算10億次翠储,改進(jìn)后1秒只能計算100萬次,速度慢了1000倍橡疼,這樣援所,所需的時間也就增加了1000倍。

那么對于我們欣除,怎么使用一個安全的hash計算方法住拭?大家可以翻閱emlog的源碼,可以在include目錄里面找到一個HashPaaword.php的文件历帚,其實這就是個類滔岳,emlog用它來計算密碼的hash。

這個類有一個特點挽牢,每次計算出的hash值都不一樣谱煤,所以黑客不能通過彩虹表等方式破解密碼,只能用這個類中一個checkpassword方法來返回用戶輸入密碼的正確性禽拔。而該函數(shù)又特意增加了計算hash的時間刘离,所以黑客很難破解他們拿到的hash值室叉。

在最新的php5.5中,這種hash算法成為了一個正式的函數(shù)硫惕,以后就能使用該函數(shù)來hash我們的密碼了太惠。

驗證碼安全性

這是我剛想到的一點,來補充一下疲憋。

驗證碼通常是由php腳本生成的隨機(jī)字符串,通過GD庫的處理梁只,制作成圖片缚柳。真正的驗證碼字符串保存在SESSION中,然后把生成的圖片展示給用戶搪锣。用戶填寫了驗證碼提交后秋忙,在服務(wù)端上SESSION中的驗證碼進(jìn)行比對。

由此想到了我之前犯過的一個錯誤构舟。驗證碼比對完成之后灰追,不管是正確還是錯誤,我都沒有清理SESSION狗超。這樣產(chǎn)生了一個問題弹澎,一旦一個用戶第一次提交驗證碼成功,第二次以后不再訪問生成驗證碼的腳本努咐,這時候SESSION中的驗證碼并沒有更新苦蒿,也沒有刪除,導(dǎo)致驗證碼重復(fù)使用渗稍,起不到驗證的作用佩迟。

再就說到了驗證碼被識別的問題,wordpress包括emlog的程序我經(jīng)常會借鑒竿屹,但他們所使用的驗證碼我卻不敢恭維报强。很多垃圾評論都是驗證碼被機(jī)器識別后產(chǎn)生的,所以我后來也使用了一個復(fù)雜一點的驗證碼拱燃,據(jù)說是w3c推薦使用的秉溉。

如果大家需要,可以到這里下載http://pan.baidu.com/s/1bnb8Zmf

好了扼雏,我能想到的坚嗜,也是在實際運用中用到的東西也就這么多了。這也僅僅是我自己寫代碼中積累的一些對代碼安全性的一個見解诗充,如果大家還有更好的想法苍蔬,可以和我交流。希望大家也能寫出更安全的代碼蝴蜓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碟绑,一起剝皮案震驚了整個濱河市俺猿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌格仲,老刑警劉巖押袍,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凯肋,居然都是意外死亡谊惭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門侮东,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圈盔,“玉大人,你說我怎么就攤上這事悄雅∏茫” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵宽闲,是天一觀的道長众眨。 經(jīng)常有香客問我,道長容诬,這世上最難降的妖魔是什么娩梨? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮放案,結(jié)果婚禮上姚建,老公的妹妹穿的比我還像新娘。我一直安慰自己吱殉,他們只是感情好掸冤,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著友雳,像睡著了一般稿湿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上押赊,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天饺藤,我揣著相機(jī)與錄音,去河邊找鬼流礁。 笑死涕俗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的神帅。 我是一名探鬼主播再姑,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼找御!你這毒婦竟也來了元镀?” 一聲冷哼從身側(cè)響起绍填,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎栖疑,沒想到半個月后讨永,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡遇革,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年卿闹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萝快。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡比原,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杠巡,到底是詐尸還是另有隱情,我是刑警寧澤雇寇,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布氢拥,位于F島的核電站,受9級特大地震影響锨侯,放射性物質(zhì)發(fā)生泄漏嫩海。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一囚痴、第九天 我趴在偏房一處隱蔽的房頂上張望叁怪。 院中可真熱鬧,春花似錦深滚、人聲如沸奕谭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽血柳。三九已至,卻和暖如春生兆,著一層夾襖步出監(jiān)牢的瞬間难捌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工鸦难, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留根吁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓合蔽,卻偏偏與公主長得像击敌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辈末,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容