PHP動物書總結09-13

9.圖像 10.PDF 11.XML
這三章用到的時候再看php

12.安全

1.過濾輸入

全部非程序生成的數據均可能是惡意的,都須要過濾
過濾時要判斷輸入數據大小長度是否合適,是否在規定的有效數據集當中
先使用靜態條件過濾,再使用數據庫過濾
示例:html

//顏色值必須在規定的顏色集當中
if (!in_array($color, $color_set)) {
    return false;
}
//中文名長度必須在3到10之間
$len = mb_strlen($cn_name);
if ($len >= 10 || $len <= 3) {
    return false;
}
//英文名只容許字母、空格、單引號、連字符
if (!preg_match('/[^A-Za-z \'\-]/'), $en_name) {
    return false;
}
2.跨站腳本攻擊XSS

XSS是最多見的網頁程序安全漏洞,它容許惡意web用戶將惡意代碼植入到提供給其它用戶的頁面中,惡意代碼能夠在其它頁面上執行竊取信息等操做。
爲了預防XSS,須要從輸出中轉義上下文:mysql

$html = array(
    'username' => htmlentities($_POST['username'], ENT_QUOTES, 'UTF-8'),
);
echo $html['username'];

應該過濾輸入來提供冗餘保護,但不能依賴過濾,由於它不是問題的根源(根源在輸出,因此輸出要轉義才行)。
關於html實體:http://www.w3school.com.cn/html/html_entities.asp
有些符號想要顯示在頁面上,好比<,在html文件中不能寫<,會被瀏覽器當作html標籤處理,正確的方法是使用<對應的html實體符號&lt。程序員

3.SQL注入

(1)示例:web

$username = $_POST['username'];
$hash = hash($_POST['password']);
$sql = "SELECT count(*) FROM users WHERE username = '{$username}' and password = '{$hash}' ";

這裏的問題是沒有轉義用戶名,它的值能夠篡改SQL查詢的格式,鑑於此漏洞很常見,不少攻擊者會使用以下用戶名:
chris' --
這樣就能夠不用密碼就能夠用chris訪問帳戶
篡改後的SQL查詢是:sql

SELECT count(*) FROM users WHERE username = 'chris' --' AND password = 'xxx

兩個破折號(--)表示sql註釋,這個查詢等同於:數據庫

SELECT * FROM users WHERE username = 'chris'

(2)SQL注入出問題的是plain sql,使用參數綁定param bind sql能夠很好解決這個問題
bind param sql防SQL注入原理:數據庫服務器會把完整參數當作sql語句的參數來執行sql語句,參數的任何部分都不會被執行,這樣就不會出現SQL注入的問題了
因此,全部根據用戶輸入數據進行sql查詢的地方理論上都應該使用param bind sql
(3)防止針對plain sql的SQL注入,能夠在拼接參數以前對參數過濾和轉義。示例以下:瀏覽器

if (preg_match(...), $username) {
    return false;
}
$username = mysql_real_escape_string($username);

實際上,爲了安全-不僅是防止SQL注入的目的,全部用戶輸入數據都應該過濾安全

4.文件名攻擊

能夠經過傳入特殊的文件名進行攻擊
(1)使用相對路徑訪問特殊文件
好比想要訪問用戶資料文件,文件放在/usr/local/目錄下,以用戶名做文件名,例如:服務器

include("/usr/local/{$username}");

若是傳入的$username值爲../../etc/passwd做爲用戶名,就可能輸出/etc/passwd的內容
(2)使用遠程文件
默認狀況下,php能夠用打開本地文件的函數打開遠程文件,fopen、include、require等函數能夠經過傳遞URL作文件名打開遠程文件,例如:

chdir('/usr/local/');
$fp = fopen($username, 'r');

若是$username的值爲http://www.example.com/myfile,打開的就是一個遠程文件,而不是本地的
若是容許用戶告訴你要include哪一個文件,狀況會更糟糕,例如:

$file = $_REQUEST['theme'];
include($file);

若是用戶傳遞了http://www.example.com/badcode.inc做爲theme字段的值,而且variables_order配置包括GET或POST,php腳本將會加載遠程代碼執行
有多種方法能夠檢查文件名限制此類攻擊:禁用遠程文件訪問、用realpath和basename檢查真正的文件名是否和傳入參數一致、配置open_basedir選項限制超出網站目錄的文件系統訪問
例子:

$filename = $_POST['username'];
$vetted = basename(realpath($filename));
if ($filename !== $vetted) {
    die("{$filename} is not a good username.");
}
include("/usr/local/{$filename}");

 

5.會話攻擊

攻擊者嵌入會話標識符的連接:

<a href="http://host/login.php?PHPSESSID=1234">Log In</a>

受害者單擊了連接以會話標識符1234繼續訪問,若是受害人進行了登陸,攻擊者能夠劫持受害者的會話來提高權限
還有一些這種攻擊的變體,好比用cookie達到一樣的目的
防禦很簡單,當權限等級改變時,好比用戶登陸,用session_generate_id()從新生成會話標識符:

if (check_auth($_POST['username'], $_POST['password'])) {
    $_SESSION['auth'] = TRUE;
    session_regerate_id(TRUE);
}

 

6.文件上傳攻擊

(1)不要相信瀏覽器提供的文件名
瀏覽器可能會提供諸如/etc/passwd、/home/ramus/.forward的文件名
能夠在與用戶交互中使用瀏覽器提供的名字,可是要本身生成惟一的名字用做實際調用的文件
例子:

$browserName = $_FILES['image']['name'];
$tempName = $_FILES['image']['tmp_name'];
echo "Thanks for sending me {$browserName}";
$counter++;
$filename = "image_{$counter}";
if (is_uploaded_file[$tempName]) { //使用is_uploaded_file確保是上傳的文件
    move_upload_file($tempName, "/web/images/{$filename}");
} else {
    die("There was a problem processing the file.");
}

(2)堤防文件系統填充
攻擊者可能會上傳很大的文件製造拒絕服務攻擊
在php.ini中post_max_size選項設置請求最大尺寸,同時須要在相應的Apache/Nginx設置文件尺寸

(3)限制訪問特定目錄
php.ini中的open_basedir選項能夠限制php只能操做該目錄和它的子目錄

(4)儘可能不使用文件,而使用數據庫
因爲一個機器上php腳本都使用同一用戶(www/nobody)運行,因此不一樣用戶的文件、同一機器上不一樣網站的文件能被其它用戶或網站訪問到,好比存儲在/tmp/sess_id的用戶會話文件就可能被其它用戶篡改或添加

(5)隱藏PHP代碼庫

若是網站根目錄時/home/httpd/html,全部該目錄下的文件均可以經過URL下載到,代碼庫、配置文件、日誌文件和其它數據應該放在該目錄外面,好比放進/home/httpd/myapp。同時應該在Web服務器配置,不能夠下載.php等文件。

7.特殊函數

eval、exec、system等執行腳本的函數要謹慎使用,由於很容易被攻擊者利用,若是用不到的話應該在php.ini禁用掉

13.應用技術

(1)代碼庫
利用經常使用的代碼構建代碼庫,把涉及到的函數放到一個php文件裏。例如一個輔助建立HTML表單元素的庫文件:其中一個函數建立textarea,一個函數建立設置日期時間的彈出表單
(2)模板系統
模板系統分離php代碼和頁面佈局,使設計者專一於頁面設計,程序員專一於數據邏輯。模板系統最基本的思想是網頁包含可被動態內容替換的特殊標記。
Smarty是一個高效的模板系統(須要研究下)
(3)輸出緩衝
默認php使用echo或相似的命令在每一個命令執行完後將結果發送到瀏覽器,能夠用php的輸出緩衝函數來收集想要發送到瀏覽器的數據,這樣能夠獲取內容長度、調整內容、批量延遲輸出、清空內容、壓縮內容等。

ob_start([callback]) //callback是後處理函數,緩衝區刷新(flush)後會向函數傳遞收集到的輸出,而且返回一個將要發送到瀏覽器的字符串。

ps:輸出緩衝用來攔截輸出,也包括var_dump等內置函數的輸出
(4)錯誤報告
正常的,當php腳本發生錯誤時,錯誤信息會被插入到腳本的輸出中,若是錯誤時致命的,腳本將會中止執行
有三種錯誤等級:提示notice、警告warning、錯誤error,error包括解析錯誤parse error,除了parse error外都是運行時錯誤
默認,除了notice都會捕獲而且展現給用戶,但能夠在php.ini中使用error_reporting選項指定輸出級別,或在腳本中使用error_reporting()函數來指定
全部錯誤error:E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR
除notice外的全部錯誤:E_ALL & ~E_NOTICE
使用@符號能夠禁止語句輸出錯誤信息(只能抑制輸出錯誤信息,不能抑制錯誤形成的結果,致命錯誤仍然會致使程序中止運行)
(5)錯誤處理和記錄
可使用set_error_handler()函數註冊錯誤處理器,當錯誤發生時,錯誤處理器會被觸發。同時可使用error_log()函數,把錯誤記錄到管理員放置日誌的地方。
例子:

//日誌分流錯誤處理器
function log_roller($error, $errorString) {
    $file = '/var/log/php_errors.log';
    if (filesize($filesize) > 1024) { //保證日誌不會大於1k
        rename($file, $file . (string)time()); 
        clearstatcache();
    }
    error_log($errorString, 3, $file);
}
set_error_handler('log_roller');
for ($i = 0; $i < 5000; $i++) {
    trigger_error(time() . ':Just an error\n');
}

php.ini中配置不顯示錯誤,而是把錯誤輸出到日誌文件中

display_errors=off
log_errors=on
error_log=/tmp/errors.log

(6)性能調優
開始不要太關注優化,先讓代碼工做起來,而後找到慢的部分去優化。
優化代碼的目標是縮短代碼運行時間和內存佔用,優化的代碼可讀性可能會差一些。
基準測試benchmark
能夠用Apache基準測試工具ab來作性能測試

./ab -c 10 -n 1000 http://localhost/info.php //10個併發請求執行1000次

查看代碼運行時間

$start = microtime();
phpinfo();
$end = microtime();
echo $end - $start; //執行時間

 

問題:tcp rpc相對於http rpc的優勢是什麼?

相關文章
相關標籤/搜索