今天我說一下怎麼在架構中over掉這些安全問題。
首先是SQL注入,這個如果你使用的是PDO,我覺得應該沒什麼問題,如果你使用的還是mysql_*等API,那麼你可以在架構中實現bindParameter或者在插入資料庫之前進行字串轉義。
前兩天把上一篇文章寫完之後,Vian在後面留言說到過SQL注入的一個解決方案,就是在在插入DB之前進行'''.addslashes($id).''',它的意思就是首先進行addslashes操作,之後再強制單引號包裹,這樣它就是一個不折不扣的字串了,所以就注入不了,我覺得這個方法不錯,贊一個!!
由於SQL注入需要聯絡到模型,XSS需要聯絡到視圖,這兩塊兒我都沒有開始講,所以我再後面再講怎麼在架構中解決,當然,如果我寫到後面忘記了,你也可以提醒我一下。
上一次我講CSRF的時候,並沒有給出一個解決方案,今天我就給出這個解決方案。實際上解決的方法很簡單,就是給它產生一個隨機數,然後後端判定傳遞過來的數和正確的數是否吻合, 如果不吻合,就不執行相應的代碼了,這個隨機數我們稱為token。
為了簡單,我們就將產生token和得到token的函數都寫在控制器中,即Controller.php。
首先是產生隨機數,最簡單的方式是使用mt_rand()直接產生一個整數,但在這兒我使用之前我在initphp這個架構中看到的解決csrf的方法,在這兒,也謝謝initphp作者的思路:
initphp的代碼是:
1 |
private function set_token() { |
2 |
if (!$_COOKIE['init_token']) { |
3 |
$str = substr(md5(time(). $this->get_useragent()), 5, 8); |
4 |
setcookie("init_token", $str, NULL, '/'); |
5 |
$_COOKIE['init_token'] = $str; |
為了簡單,我這兒就不使用userAgent了,initphp是將目前時間戳和userAgent拼接成字串之後再md5加密,取出第5到8位,我這邊的思路是將目前時間戳進行md5加密,然後從第0位開始取,取得的字串長度是隨機產生的:
1 |
$token = substr(md5(time()),0,mt_rand(10,15)); |
為了防止隨機數太大或太小,我設定mt_rand的取值範圍為10到15,也就是說產生的token的位元為10到15位。
產生token之後其他的事情就好辦了,當然,首先,也是設定token,我們沒有必要每次使用者請求的時候都產生一個隨機數,所以我們將它存放在COOKIE中,架構載入的時候會判定是否有token,如果沒有則動態產生一個,當然,產生的token會在一段時間之後到期失效,我這兒設定的時間為7天。
1 |
private function _setToken() { |
2 |
if(empty($_COOKIE['_csrfToken'])) { |
3 |
$token = substr(md5(time()),0,mt_rand(10,15)); |
4 |
$this->_token = $token; |
5 |
setcookie('_csrfToken',$token,time() + 3600 * 24 * 7); |
7 |
$this->_token = $_COOKIE['_csrfToken']; |
由於產生token的過程是架構自動完成的,所以沒有必要讓使用者看到此過程,所以將這個函數設為私人,然後在Controller類的建構函式中調用即可。
剛才是產生token,那麼怎麼得到token呢,實際上得到token的方法就非常簡單了,就是一個簡單的getter:
1 |
protected function _getToken() { |
現在我再示範一下在使用者編寫的控制器的判定過程:
假設使用者請求的URL是:http://localhost/index.php?c=Index&a=test&token=rwerdfdsfsdfs
那麼這個控制器的類的代碼如下:
02 |
class IndexController extends Controller { |
03 |
public function test() { |
04 |
$token = empty($_GET['token']) ? '' : $_GET['token']; |
05 |
if($token === $this->_getToken()) { |
08 |
$this->_redirect(array( |
可能有人會問URl上面的token值是怎麼設定然後傳遞過來的呢?
我們可以想一下,假設上一個頁面是Index控制器的test2這個Action,那麼我們可以在test2這個Action中首先使用$this->_getToken得到token值,然後在將資料傳遞到視圖,視圖中使用了之後,使用者點擊這個連結就可以將這個token值傳遞過來了。
我現在提一個問題,假設使用者訪問A頁面的時候得到token,這個token還有兩秒就到期了,這個使用者三秒之後點擊這個含有token的連結到達B頁面,B頁面由於COOKIE中的token已經失效,所以重新產生一個token,然後再和傳遞的這個token比較,自然不匹配,然後就跳轉了,這還不是有問題的呢,那麼怎麼解決呢?
由於還有一點時間,所以我提一下上傳檔案漏洞吧,使用者上傳一個比如test.php頁面,如果使用者沒有做檔案類型的判定,使用者上傳這個php檔案之後,按照連結訪問這個頁面,有可能這個頁面中有一些破壞性的代碼,整個網站就危險了。
可能你已經在程式中判定了,只允許尾碼為jpg,png,gif這三種類型,那麼我可以將這個jsp頁面尾碼改成如jpg,上傳成功之後,如果網站存在某種漏洞能夠讓它修改檔案尾碼,那麼你的網站又危險了!!
還假設你的網站不允許修改檔案尾碼名,但是它在上傳的圖片後面加上一段JS指令碼或者在上傳的檔案名稱上面寫一些指令碼,這些都可能很危險!!