上次講到控制器怎麼樣將資料傳遞到視圖,今天我就主要說一下在程式中怎麼讓代碼更“安全”,之後就轉到講模型,再講怎麼做視圖,最後再講控制器的功能強化。
我再聲明一下,我寫這個文章只是讓大家對PHP的架構編寫有一個基本的瞭解,由於本人技術有限,這個文章是給PHP初學者學習的,所以高手勿噴,還有就是我現在時間也有限,所以每次可能需要兩三天才能寫一篇,每篇我寫的時間也要控制在一個小時以內,由於邊寫這個文章邊編代碼,所以代碼中可能會存在很多BUG,見諒!!
如果你是一個PHP愛好者,請在文章後面積極回複一下,這種交流不僅可以使我的PHP技術提高,也鼓勵了我繼續寫下去的勇氣,謝謝!!
很多人編寫PHP代碼什麼都不注意,遇到很多警告,就直接通過error_reporting屏蔽掉,這樣做我覺得問題是非常大的,比如:
如果通過GET方式傳遞的參數有a,那麼程式非常正常,但是如果沒有傳遞呢,那就會拋出一個警告!!
我的作法是首先將error_reporting設定為E_STRICT,不允許程式出現警告!!
剛才這段代碼可能就需要修改成:
2 |
$a = isset($_GET['a']) ? $_GET['a'] : ''; |
除了這種問題,還有就是PHP特有的 @符號 ,很多人都喜歡用這個來屏蔽錯誤,但是我覺得使用這個弊大於利,因為當項目很大的時候,出現一個錯誤,由於這個錯誤又被屏蔽,要找到這個錯誤的位置真心的很難!!
關於異常的處理,雖然try catch會帶來很大的開銷,我個人覺得為了程式的健壯性,必要的try catch還是需要的。
好吧,雜七雜八的說了這麼多,貌似這個和安全不太沾得上邊,但對於我來說,它們也是“安全”的一部分。
現在假設你花費了十天時間編寫了一個簡單的部落格系統,購買了萬網的虛擬機器主機或VPS,申請網域名稱,網站備案,然後部署代碼,這一切的一切都搞定了,然後使用者就可以通過比如www.test.com這樣一個網域名稱來訪問你的部落格系統,你這套部落格系統很受歡迎,短時間內就積累了大量的人氣,但是突然有一天,你發現你的網站突然出故障了,你怎麼辦?
線上上將PHP的設定檔中的error_reporting開啟,然後線上調試?
說實話,我之前也在我的部落格系統上麵線上調試過,和上述情況不一樣的一點是,我的部落格訪問量很低,因為我這個人太懶了,不太喜歡去管理我的部落格。
如果你的網站擁有很大的訪問量,你線上上做調試想想也是不可能的事情,那怎麼做呢?
記Log,如果你的網站在發生故障之前你就有寫Log,那麼程式出現故障之後你只需要開啟記錄檔,然後就可以看到故障出現的位置,然後修複掉,這樣就OK了!!
好了,現假設我是你的同學,並且也參與了你的部落格系統的開發,但是我和你前一陣鬧了一點矛盾,我懷恨在心,想把你的部落格系統破壞掉,怎麼破壞呢?
首先假設你的資料庫名為Test,這個資料庫中存在一個user表,user表存放著20000個會員資訊,我知道你的部落格註冊系統的代碼是如下:
02 |
$username = $_POST['username']; |
03 |
$password = $_POST['password']; |
04 |
if(empty($username) empty($password)) { |
05 |
//跳轉到註冊介面並提示使用者名稱或密碼未填寫 |
09 |
//假設DB類封裝了很多SQL操作,析構的時候自動關閉資料庫連接,具體過程不寫了 |
10 |
//$db是一個資料庫DB類的執行個體,存在兩個方法 |
11 |
//$db->isUsernameExists判定是否使用者名稱是否存在 |
12 |
//$db->query 執行一條SQL語句 |
13 |
if(!$db->isUsernameExists($username)) { |
14 |
$db->query("insert into user (username,password) values ('" . $username . "','" . $password. "')"); |
這段代碼有問題嗎,我相信很多PHP Coder都會很鄙視的說到“你不就是想說SQL注入嘛”。
的確,這個就是一個SQL注入的問題,這個問題已經很古老了,好像大家都知道,為什麼我還要講呢?
這是因為我之前在學校看到過幾個由學弟編寫的PHP項目,他們就基本上沒有考慮過這個問題,很多代碼就直接這麼寫,當然,你如果按照網上SQL注入的方式去試,會發現你根本注入不了,貌似PHP已經自動幫你解決掉這個問題了,怎麼解決的呢,實際上就是對特殊字元前加上反斜線。
首先說一下為什麼SQL注入失敗呢?如果你的php.ini中配置了自動轉義,PHP會在你將資料插入到DB之前對資料進行轉義。
貌似這樣我們就不用考慮這個問題了,但是實際上PHP幫我們做了這些才讓事情更可怕,如果你將你的程式轉移到另外一台linux伺服器, 這台伺服器上面php.ini設定檔中配置了不自動進行轉義,那麼你的程式一下子問題就大了,我們不應該將我們代碼的安全性依賴於伺服器的配置。那麼怎麼搞定這個事情呢?
幸好,PHP中已經有了addslashes函數,它會對特殊字元進行轉義,但是很遺憾,通過查看PHP手冊發現:
預設情況下,PHP 指令 magic_quotes_gpc 為 on,它主要是對所有的 GET、POST 和 COOKIE 資料自動運行addslashes()。不要對已經被 magic_quotes_gpc 轉義過的字串使用 addslashes(),因為這樣會導致雙層轉義。遇到這種情況時可以使用函數 get_magic_quotes_gpc() 進行檢測
那怎麼做呢,幸好PHP已經提供了一個get_magic_quotes_gpc函數可以來判定是否已經開啟了magic_quotes_gpc,所以我們可以自訂一個addslashes函數,如:
2 |
function myAddslashes($str) { |
3 |
if(get_magic_quotes_gpc()){ |
4 |
return addslashes($str); |
其實還有另外的方法解決這個問題:
1. 使用PDO來訪問DB,PDO中可以使用PDOStatement->bindParam,這樣,PDO會自動幫你做好這一切,並且我個人覺得PDO很有前途!!
2. 如果get_magic_quotes_gpc為on,首先調用stripslashes去除逸出字元,然後在插入資料庫之前使用mysql_real_escape_string,我個人覺得這種方式比第一種方式靠譜!!
當然,說了這麼多,有可能還有童鞋不知道什麼是SQL注入,我就簡略的講一下SQL注入的過程啊,熟悉SQL注入的人直接pass掉這一段。
按照上面的例子,假設使用者在password這個欄位輸入的值為a');drop table user;...,那麼執行SQL的時候SQL語句就會變成:
insert into user (username,password) values ('使用者名稱','a');drop table user;...')
這個SQL首先會向user表插入一條記錄,然後刪除整個表,然後。。。。SQL出錯了。
不過不管SQL是否出錯,user表已經沒有了,對於一個會員10000的部落格,使用者表沒有了,我覺得損失還是蠻大的,當然,你也可以將串連資料庫的使用者的許可權降低,沒有刪除表的許可權,但是這樣也不是一個治本的方法,還是解決掉SQL注入漏洞比較靠譜。
好,解決掉SQL注入,我再說一下XSS(跨站指令碼漏洞)的問題。
現有一段PHP的指令碼:
我才講到這個的代碼是有問題的,上面說的是有時候會拋出警告,但是如果傳遞參數的時候被不法分子利用,這個問題就大多了。
現在假設訪問這個指令碼的URL是:http://localhost/test.php?a=a,我將參數a的值設為a,傳遞過去一點問題都沒有吧,但是現在假設我值換一下,URL變成了:
http://localhost/test.php?a=<script>location.href="http://www.tmall.com"</script>,那麼執行指令碼的時候就會跳轉到天貓首頁,這樣恐怖吧!!
如果這個不是跳轉到天貓,而是跳轉到某一個駭客設好的網址,他就可能將你的Cookie資訊弄到,然後就可以偽造Cookie,用你的身份登入部落格系統,然後。。。。你懂的。
解決這個問題的方法也很簡單,就是字串轉義就OK,實際上就可以通過我們自訂的這個myAddslashes方法來做,調用了這個方法之後,指令碼無法執行了,但是有時候我們又需要執行指令碼,那怎麼做呢,我們可以對輸入的字串按照一定的規則過濾,具體怎麼使用的可以參照手冊。
解決掉這個問題之後,我再說另外一個問題,這個問題就是CSRF(跨網站請求偽造漏洞),這是個什麼東東!!!
現假設你有一個留言的系統,留言的內容是富文本的,使用者可以添加表情等等,表情的HTML代碼是<img src = "XXX" />,假設使用者填寫的表情是通過你提供的富文字編輯器來做的,沒有任何問題,但是如果他不使用這個,而是利用img標籤做了另外一個事情呢?
怎麼做呢?很簡單的,就是改變img標籤的src屬性:
<img src = "http://www.tmall.com" />
提交留言之後發現這個圖片無法顯示,為什麼無法顯示其實也很簡單,根本不是一個合法的圖片連結,但是當一個不知情的使用者A查看留言的時候,會發生什麼情況,每次使用者開啟這個留言的頁面,實際上就會訪問www.tmall.com一次,如果將這個網址改成駭客的網址,那麼結果,還是你懂的。。。
其實除了這些,還有上傳檔案的漏洞等等,由於時間有限,就不說了。
我講這些實際上就是為了說明,安全問題實際上很重要,我們在編程式的時候要考慮的東西實際上是很多的。
本來今天還要講怎麼在架構中怎麼去解決這些問題,但是又超出我預計的一個小時的時間了,那就下次再說了。