怎麼一步步編寫簡單的PHP的Framework(十四)

      今天我說一下怎麼在框架中over掉這些安全問題。 php

      首先是SQL注入,這個若是你使用的是PDO,我以爲應該沒什麼問題,若是你使用的仍是mysql_*等API,那麼你能夠在框架中實現bindParameter或者在插入數據庫以前進行字符串轉義。 mysql

      前兩天把上一篇文章寫完以後,Vian在後面留言說到過SQL注入的一個解決方案,就是在在插入DB以前進行'''.addslashes($id).''',它的意思就是首先進行addslashes操做,以後再強制單引號包裹,這樣它就是一個徹徹底底的字符串了,因此就注入不了,我以爲這個方法不錯,贊一個!! 程序員

      因爲SQL注入須要聯繫到模型,XSS須要聯繫到視圖,這兩塊兒我都沒有開始講,因此我再後面再講怎麼在框架中解決,固然,若是我寫到後面忘記了,你也能夠提醒我一下。 sql

      上一次我講CSRF的時候,並無給出一個解決方案,今天我就給出這個解決方案。實際上解決的方法很簡單,就是給它產生一個隨機數,而後後端斷定傳遞過來的數和正確的數是否吻合, 若是不吻合,就不執行相應的代碼了,這個隨機數咱們稱爲token。 數據庫

      爲了簡單,咱們就將產生token和獲得token的函數都寫在控制器中,即Controller.php。 後端

      首先是生成隨機數,最簡單的方式是使用mt_rand()直接產生一個整數,但在這兒我使用以前我在initphp這個框架中看到的解決csrf的方法,在這兒,也謝謝initphp做者的思路: 安全

      initphp的代碼是: cookie

private function set_token() {
		if (!$_COOKIE['init_token']) {
			$str = substr(md5(time(). $this->get_useragent()), 5, 8);
			setcookie("init_token", $str, NULL, '/');
			$_COOKIE['init_token'] = $str;	
		}
	}
       爲了簡單,我這兒就不使用userAgent了,initphp是將當前時間戳和userAgent拼接成字符串以後再md5加密,取出第5到8位,我這邊的思路是將當前時間戳進行md5加密,而後從第0位開始取,取得的字符串長度是隨機產生的:


$token = substr(md5(time()),0,mt_rand(10,15));
      爲了防止隨機數太大或過小,我設置mt_rand的取值範圍爲10到15,也就是說產生的token的位數爲10到15位。


      生成token以後其餘的事情就好辦了,固然,首先,也是設置token,咱們沒有必要每次用戶請求的時候都產生一個隨機數,因此咱們將它存放在COOKIE中,框架載入的時候會斷定是否有token,若是沒有則動態生成一個,固然,生成的token會在一段時間以後過時失效,我這兒設置的時間爲7天。 框架

       

private function _setToken() {
		if(empty($_COOKIE['_csrfToken'])) {
			$token = substr(md5(time()),0,mt_rand(10,15));
			$this->_token = $token;
			setcookie('_csrfToken',$token,time() + 3600 * 24 * 7);
		} else {
			$this->_token = $_COOKIE['_csrfToken'];
		}
	}


      因爲生成token的過程是框架自動完成的,因此沒有必要讓用戶看到此過程,因此將這個函數設爲私有,而後在Controller類的構造函數中調用便可。 jsp

      剛纔是生成token,那麼怎麼獲得token呢,實際上獲得token的方法就很是簡單了,就是一個簡單的getter:


protected function _getToken() {
		return $this->_token;
	}
      如今我再演示一下在用戶編寫的控制器的斷定過程:


       假設用戶請求的URL是:http://localhost/index.php?c=Index&a=test&token=rwerdfdsfsdfs

       那麼這個控制器的類的代碼以下:


<?php
class IndexController extends Controller {
	public function test() {
		$token = empty($_GET['token']) ? '' : $_GET['token'];
		if($token === $this->_getToken()) {
			//斷定爲正常
		} else {
			$this->_redirect(array(
				//跳轉到某一個控制器的某一個Action
			));
		}
	}
}
        可能有人會問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腳本或者在上傳的文件名上面寫一些腳本,這些均可能很危險!!

          因此,作好一個WEB應用不是想象中那麼容易的,剛纔說的是安全這一起的內容,實際上當訪問量大了以後,以前你以爲徹底不是問題的問題可能就變成了一個大的問題,除了這兩個,還有其餘N個問題,因此,保持低調,繼續學習,繼續提升。。。。

         上面一段話僅以自勉,其餘人能夠忽略。

          大四了,還有一個月就要離開天貓回學校作畢設了,我很想在畢業以前給個人網站作一個大幅度的修改,可是我如今不知道到底要作什麼,不過確定這個網站是針對程序員的,但願你們提一點意見,個人我的網站http://www.qingyueit.com(比較挫,很久都沒有更新文章了,甚至連界面也是直接用了別人的主題)。

         本次代碼點此下載

相關文章
相關標籤/搜索