# PHP 安全備忘單
> 譯者:[fisherMartyn](https://github.com/fisherMartyn)
> 來源:[PHP-SECURITY-CHEAT-SHEET](https://github.com/fisherMartyn/PHP-SECURITY-CHEAT-SHEET)
# 介紹
這篇博客是翻譯自OWSAP的<a href ="https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet">PHP Security Cheat Sheet</a>,你可以查看原文。
本文章的目的是提供基本的PHP安全建議,主要針對開發者和系統管理員。需要注意的是這里提到的安全建議可能并不足已支撐安全的網絡應用,而只是一個組成部分。
## PHP介紹
根據W3 Techs的統計,PHP是最流行的Server端編程語言,大約81.8%的Web Server用PHP部署(任何語言都要為自己吹一把的)。
與其它語言不同,PHP即是一門語言,也是一個web框架,在PHP語言中內嵌了典型的web框架的特性。跟其它語言一樣,PHP背后也有龐大的社區軟件庫,貢獻了PHP安全和其它各個方面的特性。所有的這三個方面(語言、框架和開源庫)都是在構建安全的web應用中需要考慮的因素。
PHP一直在發展中,而不是固定的設計,因此你很可能寫出了不安全的PHP程序。為了讓應用更安全,你需要了解所有的陷阱。
### 語言相關
#### 弱類型
PHP是弱類型的,意味著不正確的數據類型會被自動轉換。這個特性經常會隱藏開發中的錯誤信息或者不規范數據的注入,導致系統風險性增加。(例子在下面的輸入處理部分)。
盡量使用非類型轉換的函數和操作符(例如用===替換==)。但并非所有的操作符都有嚴格類型檢查的版本(例如<=和>=),另外很多內置的函數(例如in_array)默認進行類型轉換,給編寫安全的代碼帶來了挑戰。
#### 異常和錯誤處理
幾乎所有的PHP內置函數很多PHP庫,不支持異常,而是用其它的報警機制(例如notice),允許出錯的代碼繼續運行。這也會隱藏很多bug。很多其它的語言、大部分高級語言,相對于PHP,開發者代碼錯誤或者開發者未處理的運行時錯誤,都會導致程序停止執行,從而保護程序。
考慮下面的程序,程序的目的是檢查一個username是不是在數據庫的黑名單中,以限制訪問。
```
$db_link = mysqli_connect('localhost', 'dbuser', 'dbpassword', 'dbname');
function can_access_feature($current_user) {
global $db_link;
$username = mysqli_real_escape_string($db_link, $current_user->username);
$res = mysqli_query($db_link, "SELECT COUNT(id) FROM blacklisted_users WHERE username = '$username';");
$row = mysqli_fetch_array($res);
if ((int)$row[0] > 0) {
return false;
} else {
return true;
}
}
if (!can_access_feature($current_user)) {
exit();
}
// Code for feature here
```
上面的代碼可能產生各種各樣的運行時錯誤,例如由于用戶名和密碼錯誤或者數據庫server宕機可能導致的數據庫連接失敗、或者連接可能被server斷開。在上述情況下,`mysqli`函數會拋出notice或者warnings,但不會拋出異常或者fatal error,代碼會繼續執行。變量`$row`會成為`NULL`,基于若類型轉換`(int)$row[0]`會成為0,最終`can_access_feature`會返回`true`,用戶會獲得訪問權限,不管用戶是否在黑名單中。
如果使用native的數據庫API,需要在各個地方添加錯誤檢查。然而由于這需要額外的工作、又如此容易被忽略,所以它是'默認不安全的'。這就是為什么訪問數據庫通常要采用<a href="https://secure.php.net/manual/en/intro.pdo.php">PHP Data Objects (PDO)</a>和其錯誤處理<a href="https://secure.php.net/manual/en/pdo.error-handling.php"> ERRMODE_WARNING or ERRMODE_EXCEPTION flags </a>,除非有特殊的原因,不會使用native API和錯誤檢查工作。
使用<a href="http://www.php.net/manual/en/function.error-reporting.php">error_reporting</a>函數將錯誤優先級設置越高越好,修復warnings,讓系統越魯棒越好。
#### php.ini
PHP代碼的行為經常強依賴于很多配置文件的設置,例如基本的錯誤處理配置等。這使得很難寫出在所有場景下正常運行的代碼。不同的庫依賴不同的設置,使得很難使用第三方的代碼,這在后面的'配置'部分也會提到。
#### 無用的buildin函數
PHP提供了大量的內嵌函數,例如`addslashes`、`mysql_escape_string`、`mysql_real_escape_string`等。這些試圖提供安全的特性的函數經常是有bug的,并不能解決安全性的問題。許多內嵌的函數已經被棄用或者遷移,但由于向下兼容的規則,這些函數會保留很長時間。
PHP會提供`array`的數據結構,在各種代碼中隨意使用,但由于混合了數組和字典,經常導致混淆。這種混淆經常導致甚至是有經驗的程序員引入重大的安全隱患,例如<a href="https://www.drupal.org/SA-CORE-2014-005">Drupal SA-CORE-2014-005</a> 和 <a href="http://cgit.drupalcode.org/drupal/commit/?id=26a7752c34321fd9cb889308f507ca6bdb777f08">the patch</a>。
### 框架相關
#### URL路由
PHP默認的路由機制是使用文件目錄中`.php`的后綴,這帶來很多風險
* 文件上傳沒有進行文件名過濾導致的遠程執行風險(換句話說,服務化器執行了外部的代碼)。在接收遠程文件上傳時,請確保文件名和內容經過過濾。
* 源代碼和配置文件存儲在公共可訪問的目錄中,可以被下載。誤配置或者缺少配置可能會導致源代碼或者包含密碼等信息的配置文件被攻擊者隨意下載(換句話說,服務器暴露了私密的信息)。你可以使用`.htaccess`來限制訪問,但這并不是最完美的方案,因為它是'默認不安全的',也沒有可替代的方案。
* URL路由機制只是模塊系統。這意味著攻擊者可能可以使用非法的文件名作為進入點,帶來認證機制可能被完全繞過的風險。這在PHP中極其容易,因為有全局可訪問的請求信息(例如`$_GET`),所以文件層面的代碼即可操作整個請求、而不是在固定的函數中進行請求處理。
* URL路由機制的缺失導致開發者開發各自的路由方法。這往往是不安全的,容易導致請求處理函數未進行合適的請求限制。
#### 輸入處理
PHP將HTTP的輸入轉換成了數組、而不是簡單字符串。這會導致對于數據的混淆,容易導致安全風險。例如下面的代碼是一個密碼重置的一次性機制判斷
```
$supplied_nonce = $_GET['nonce'];
$correct_nonce = get_correct_value_somehow();
if (strcmp($supplied_nonce, $correct_nonce) == 0) {
// Go ahead and reset the password
} else {
echo 'Sorry, incorrect link';
}
```
如果攻擊者使用如下的查詢字符串
```
http://example.com/?nonce[]=a
```
會導致`$supplied_nonce`成為數組、`strcmp()`函數會返回NULL(連異常都不會拋出),由于若類型轉換和沒有使用`===`操作符,校驗成功,攻擊者可以在不提供原密碼的情況下進行密碼修改。
相似的case和`array`結構導致的混淆事故,可以在<a href="https://www.drupal.org/SA-CORE-2014-005"> Drupal SA-CORE-2014-005 </a>,參見<a href ="http://www.zoubi.me/blog/drupageddon-sa-core-2014-005-drupal-7-sql-injection-exploit-demo"> example exploit</a>。
#### 模板語言
PHP本質上是一門模板語言,但它默認并沒有提供HTML轉義,在web應用中帶來極大使用困難,可以參見下面的XSS部分。
#### 其它不足
另外有很多web框架應該支持的重要特性,例如默認開啟CSRF的保護機制。因為PHP只是提供了一個基本的、功能足夠的特性來創建web站點,很多不了解CSRF保護機制的人就僅使用了PHP基本的功能。
### 第三方PHP代碼
由于上述原因,PHP項目和開源庫經常是不安全的,特別是沒有使用恰當的框架時。不要輕易相信你在網絡上發現的PHP代碼,看起來沒問題的代碼可能隱藏了很多的安全隱患。
書寫不規范的代碼會導致warning信息被隱藏,無法及時暴露問題。通常人們會關閉notice信息,但絕不要這么做,這帶來更嚴重的后果。
## 定期升級PHP
要定期升級生產環境的PHP版本。因為每天都有新的PHP漏洞被暴露出來,攻擊者經常會用這些漏洞攻擊網絡上任意的服務器。
# 配置
配置文件對PHP的行為影響極大,包括`php.ini`文件、Apache配置命令和<a href="http://www.php.net/manual/en/configuration.php">運行時機制</a>。
有很多不安全的設置,下面是一部分。
## setHandler
setHandler命令可以配置PHP代碼,在很多場景中,它被用`addHandler`指令錯誤的替代,`addHanler`可以正常工作,但也會使很多其它文件像PHP一樣執行,例如一個文件名為`foo.php.ini`的文件也會被當做php來執行。這會帶來嚴重的遠程執行的風險,如果該文件是被惡意上傳。
# 不可信的數據
All input is evil,所有用戶的輸入都不值得信任。輸入必須被用合適的方式校驗、或者過濾。
`$_SERVER`、`$_GET`、`$_POST`、`$_REQUEST`、`$_FILES`和`$_COOKIE`這些超全局變量也不應該被信任。盡管`$_REQUEST`中并非所有的數據都可以被用戶偽造,但還是有相當部分的可能被偽造,特別是處理HTTP header的那部分(以HTTP_開頭的)。
## 文件上傳
來自用戶上傳的文件會帶來巨大的安全隱患,特別是該文件可以被其他用戶下載時。例如:
* 任何HTML文件可能會導致xss攻擊。
* 任何PHP文件可能會帶來遠程代碼執行的風險。
由于PHP對于代碼執行的檢查并不嚴格(帶上合適的擴展名即可),對于使用PHP搭建的web服務器一定要確保上傳進行合適的文件名過濾后再進行保存。
## 處理$_FILES數組的常見錯誤
如下的代碼片段、或者類似功能的代碼是很常見的:
```
if ($_FILES['some_name']['type'] == 'image/jpeg') {
//Proceed to accept the file as a valid image
}
```
然而`type`并不是啟發式的去校驗的,而僅僅是接收了HTTP請求中的數據,這很容易被客戶端偽造。一個更好的去校驗文件類型的方法是使用`finfo`庫,盡管這種方法也并不完美。
```
$finfo = new finfo(FILEINFO_MIME_TYPE);
$fileContents = file_get_contents($_FILES['some_name']['tmp_name']);
$mimeType = $finfo->buffer($fileContents);
```
盡管這會占用服務器的計算資源,但`$mimeType`是更好的判斷文件類型的方式,會阻止一些用戶上傳危險的文件,并偽裝成image等形式,來造成攻擊行為。
## 使用$_REQUEST
強烈不建議使用`$_REQUEST`。這個超全局變量不僅包含了GET和POST,還包含了用戶請求的cookies等信息,所有的這些數據被組合在了一個數組里,導致很難檢測數據的來源,很容易導致混淆、易于犯錯、容易導致安全風險。
# 數據庫
單個mysql注入就會直接操縱整個網站,所以每個黑客都會首先嘗試mysql注入,防止mysql注入也是PHP安全站點中最應該考慮的,遵循如下規則:
## 不要在SQL中連接或者插入數據
### 不要直接使用用戶數據的string構造SQL
```
$sql = "SELECT * FROM users WHERE username = '" . $username . "';";
```
或者使用如下SQL:
```
$sql = "SELECT * FROM users WHERE username = '$username';";
```
如果`$username`來自不可信任的來源,它可以包含類似于`'`這樣的字符,從而可以執行其它命令,甚至刪除數據庫命令。使用prepare語句和綁定參數是更好的解決方案。PHP的<a href="http://php.net/mysqli">mysqli</a>和<a href="https://secure.php.net/pdo">PDO</a>提供了相關的特性。
### 轉義是不安全的
`mysql_real_escape_string `這種轉義是不安全的。不要依賴這個函數來防止SQL注入。
當你使用`mysql_real_escape_string `來校驗每個變量然后放入查詢語句中,你難保一次都不會忘記,只要忘記一次就會是災難。你無法保證一次都不會忘記。而且,你要保證你使用了引號,如果輸入是數字的話,這會很不自然,容易忘記。使用prepare語句或者相似的API,它們會幫你做正確的轉義和過濾。(大部分ORM框架也會做這種轉義、幫你創建SQL)
### 使用prepare語句
prepare語句非常安全。在prepare語句中,SQL命令和數據是分開的,每個用戶輸入都會認為是數據,進行處理。
相關的內容可以參考PHP的<a href="https://secure.php.net/manual/en/mysqli.quickstart.prepared-statements.php">mysqli prepare statement</a>和<a href="https://secure.php.net/manual/en/pdo.prepare.php">PDO prepare statement</a>
#### prepare語句失效的場景
進行動態的查詢、不支持prepare的變量,或者數據庫引擎不支持prepare語句會是問題。例如,PDO MySQL不支持`?`作為限制符。而且限制符不能作為表名或者列名用于`select`語句中。在此類的場景中,盡量使用框架提供的query builder,如果框架沒有功能,使用Composer和Packagist安裝幾個第三方包,不要自己做。
### ORM
ORM(Object Relational Mappers),即對象關系映射,是非常好的安全手段。如果你使用ORM(例如<a href="http://www.doctrine-project.org/">Doctrine</a>),仍然可能會遭受SQL攻擊。盡管ORM中注入攻擊比較困難,不過串聯ORM查詢和串聯SQL查詢具有相同的問題。ORM也支持prepare語句。
### 編碼事宜
#### 盡量使用UTF-8編碼
許多新型攻擊模式依賴于編碼。使用UTF-8作為數據庫和應用的字符集,除非你有對其它字符集的強依賴。
```
$DB = new mysqli($Host, $Username, $Password, $DatabaseName);
if (mysqli_connect_errno())
trigger_error("Unable to connect to MySQLi database.");
$DB->set_charset('UTF-8');
```
# 其它注入
除了SQL以外,還有PHP中別的注入的可能性
## 腳本注入
一些PHP函數例如:
* shell_exc
* exec
* passthru
* system
* 反引號(`)
上面的函數或者命令將字符串作為腳本和命令名。基于配置,腳本命令泄露可以導致應用設置和配置文件泄露,這是非常嚴重的風險,會導致整個網站被控制。
不要向這些函數傳入未經過濾的輸入,除非你確認它們一定是安全的。轉義和其他對策是無效的,攻擊者會嘗試各種測試向量,不要相信新手程序員。
## 代碼注入
所有的解釋型語言,例如PHP,具有將字符串轉換成命令并執行的能力。PHP中這個函數是`eval()`,使用`eval()`是不安全的,并不建議使用,除非你確認除了`eval`以外沒有別的選擇。`eval()`性能比較差。
不要輕易在`preg_replace()`中輸入未經過濾的輸入,因為payload會被<a href ="https://stackoverflow.com/questions/4289923/in-which-languages-is-it-a-security-hole-to-use-user-supplied-regular-expression/4292439#4292439">`eval()'ed`</a>
```
preg_replace("/.*/e","system('echo /etc/passwd')");
```
Reflection也有代碼注入的風險。這屬于高級的話題,請參考相關文檔。
## 其它注入
LDAP、XPath和其他一些使用數組作為輸入的第三方庫,也容易被注入攻擊。時刻記住有些數組不僅僅是數據,而是命令,所以在輸入第三方庫之前要進行檢查。
# XSS
提到XSS時,主要指兩個場景,分別可以通過如下方式緩解:
## 沒有tags
大部分時候,用戶不會提供未經轉義的HTML標簽。例如在一個cell或者textbox中輸出用戶數據。
如果需要使用標準的PHP來輸出(echo)模板,你可以通過對數據進行`htmlspecialchars `(或者下面的函數,對htmlspecialchars的封裝)緩解XSS。盡管這并不推薦,因為需要開發者在每個使用場景都要調用。如果開發者忘記了,就存在XSS的風險。我們認為默認不安全的方法就是不安全的。
除此之外,你可以使用默認進行HTML轉義的模板引擎。所有需要輸出到模板的HTML都需要經過HTML模板。
如果你的項目不能轉換到一個安全的模板引擎,你可以使用下面的函數來過濾數據。
需要注意的是下面的函數對于`style`、`script`、`image`、`src`、`a`等等不安全的元素是無效的。所有輸出到瀏覽器的數據,都要經過下面函數的過濾。
```
//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
return htmlspecialchars($data,ENT_QUOTES |ENT_HTML401,$encoding);
}
function xecho($data)
{
echo xssafe($data);
}
//usage example
<input type='text' name='test' value='<?php xecho ("' onclick='alert(1)");?>' />
```
## 不可信的tags
當你需要用戶在你輸出時提供HTML tags時(例如富文本博客評論、博客等等),但不是很信任使用者時,你需要使用安全編碼庫。這很困難,遷移過程也很慢,所以大量的網絡應用有XSS注入攻擊的風險。OWASP ESAPI對于不同類型數據的編碼具有大量的編解碼器,也有PHP的OWASP AntiSammy和HTMLPurifier。這需要一定的配置和學習的成本,但對于一個好的網絡應用,是必不可少的。
## 模板引擎
有一些模板引擎可以幫助開發者或者設計者輸出數據,防止XSS攻擊。盡管模板引擎的初衷并不是安全、而是增加設計的體驗,但大部分引擎輸出時自動轉義了變量,或者要求開發者顯式的指定不需要轉義的變量。這使得輸出數據默認是安全的。類似的引擎包括twig、Smarty、Haanga和Rain TPL。
模板引擎是很好的安全實踐,因為它提供了開發者容易忘記的特性,默認的進行XSS保護的轉義。對于安全很看重的系統應該使用默認安全的引擎。
## 其它建議
* 在web應用中,沒有可信任的區域。很多開發者會留著網站的admin部分不進行XSS過濾,事實上大部分攻擊者都會對admin的cookie的XSS感興趣。任何需要輸出的變量都要用上面提到的方案進行處理。刪除每個`echo`,`print`和`printf`,替換成安全的模板引擎。
* HTTP-Only cookies是很好的實踐,很快每個瀏覽器都會支持。建議現在就開始使用。
* 上面提供的函數只支持HTML語法。如果你put your Element Attributes without quotation,你就死定了。
* <a href="https://www.owasp.org/index.php/Reflected_XSS">反射型XSS</a>和普通的XSS一樣危險,可能出現在應用中任何部分。找到并解決它們。
* 并不是所有的PHP都安裝了mhash擴展,如果要做hash,需要先檢查一下。否則你不能用SHA-256
* 并不是所有的PHP都安裝了mcrypt擴展,如果沒安裝,你沒法做AES,使用前請先進行檢查。
# CSRF
CSRF是修復理論上比較簡單,但正確的修復確并不容易。首先提幾個關于CSRF的tips:
* 每個請求都是有用的,都要做CSRF的修復。不僅僅包括修改的請求、還包括執行時間較長的讀請求。
* CSRF容易發生在GET,也不要忽略POST。不要認為POST是安全的。
<a href="https://www.owasp.org/index.php/PHP_CSRF_Guard"> OWASP PHP CSRFGuard</a>這是一個代碼片段,講了如何進行CSRF的修復。僅僅復制粘貼是無效的,后面會發布一個拷貝粘貼版本。當前,使用上面代碼結合如下建議:
* 對于重要的操作(修改密碼、修改郵箱等)使用二次認證。
* 如果你不清楚你的請求是否能夠免于CSRF,使用驗證碼(盡管驗證碼會讓用戶體驗變差)
* 如果你的請求依賴于其它請求的部分內容(例如其它請求的cookies或者http header等部分),你也要對那些請求添加CSRF tokens。
* AJAX的請求需要重建CSRF tokens。使用上面提供的代碼,不要只依賴javscript。
* CSRF會導致不便,結合你的設計和架構來使用。
# 認證和SESSION管理
PHP并沒有提供一個真正可用的認證模塊,你需要基于自身的框架自行實現。然而用這種方式,很多PHP框架在這方面并不完美,因為它們都是被開源社區的開發者而不是安全專家開發出來的。下面有幾個建議:
## session管理
PHP默認的session機制是安全的,生成的PHPSessionID足夠隨機,但是存儲未必是安全的:
* Session文件默認存儲在temp目錄(/tmp),除非安裝了suPHP,否則全局可訪問。所有任何的LFI或者泄露都可以操作他們。
* 默認配置下Session是存儲在文件中的,這對于高訪問量的網站來說,太慢了。你可以存儲在memory文件中。
* 你可以自己實現session機制,不再依賴PHP本身。可以將session存儲在數據庫,利用全部、部分或者直接不用php的session功能。
### session劫持預防
將session綁定到IP地址是好的習慣,這將很大程度上解決session劫持的場景。很多用戶如果使用匿名工具(例如TOR),訪問你的系統就可能出問題。
在session第一次創建時,存儲客戶端的IP地址,確保后面的請求都是相同來源。下面的代碼返回了客戶端的IP地址
```
$IP = getenv ( "REMOTE_ADDR" );
```
在本地環境下,無法獲得有效的IP地址,可能是`:::1`或者`:::127`,調整你的IP檢查邏輯。另外要注意使用`HTTP_X_FORWARDED_FOR`變量的相似代碼,因為該數據可以被用戶隨意修改,易于被欺騙。(<a href="http://www.thespanner.co.uk/2007/12/02/faking-the-unexpected/">這里</a>和<a href="http://security.stackexchange.com/a/34327/37">這里</a>)
### 將sessionid失效
當發生異常時,需要將session相關信息失效(刪除cookie、刪除session存儲、刪除其他痕跡),記錄日志是很好的做法。很多系統還會通過郵件通知用戶。
### session重置
當替換行為發生時,需要將session進行重置(例如當用戶登錄時)。
### 暴露的session
sessonid應該是機密的,應用不應該在任何場合將它暴露在外面。不要講sessionid用在url中的一部分。當sessoin傳遞機密數據時,使用TLS,否則可能發生session劫持。
### Session Fixation
使用` session_regenerate_id()`來替換sessonid,當用戶登錄時(甚至每個請求以后)
### session過期
session應該在一定時間后失效,無論是活動還是靜止狀態。超時的操作包括在活動狀態下重置sessionid和靜止狀態下刪除session信息。
當退出登錄發生時,完成所有的工作,包括清空相關的所有sesson記錄。
#### 不活動狀態的超時
如果當前的請求在上次請求以后已經經過了超過X秒,將其超時。這種清空需要在每次請求的時候進行判斷,通常這個時間是30分鐘,但也要根據具體應用來確定。
這種超時使得用戶在一臺公共的機器登錄后忘記登出時獲得一定的安全退出機制。也一定程度上預防了session劫持。
#### 正常的超時
如果當前的session已經經過了一定的時間,即使處于活躍狀態,也將其超時。超時時間通常在一天和一周之間不等。實現該特性需要記錄session的開始時間。
### Cookies
處理PHP的cookie有一些tricks:
#### 不要序列化
不要序列化cookie中的數據,因為很容易被操作篡改,增加攻擊數據。
#### 恰當刪除
要安全的刪除cookie,使用如下的代碼:
```
setcookie ($name, "", 1);
setcookie ($name, false);
unset($_COOKIE[$name]);
```
第一行保證了瀏覽器過期了cookie數據,第二行是標準的刪除cookie的方法,第三行從腳本中刪除了cookie信息。很多手冊中使用了`time()-3600`來做超時,但在瀏覽器時間不準確的情況下會無效。
可以使用`session_name()`來獲得php默認session cookie名。
#### HTTP only
現代的瀏覽器支持HTTP-Only的cookies,這種cookie只可以通過HTTP(s)請求訪問,不能被javascript訪問,所以XSS的代碼無法訪問。這是很好的安全實踐,但是并不能很令人滿意,因為很多主流的瀏覽器發現了很多Http-Only的cookie暴露給JavaScript的bug。
PHP5.2+版本支持Http-Only cookie,你要手動設置http session cookie(不是使用session_start)
```
#prototype
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
#usage
if (!setcookie("MySessionID", $secureRandomSessionID,$generalTimeout, $applicationRootURLwithoutHost, NULL, NULL,true))
echo ("could not set HTTP-only cookie");
```
`$path`參數指定了cookie的有效范圍,例如,如果你的網址是`example.com/some/folder`,`$path`應該是`/some/folder`,否則所有在`example.com`的應用都可以讀取你的cookie。如果你使用全域名,可以忽略。域名參數強制了可訪問域名,如果需要在多個域和IP訪問,忽略該參數,否則老老實實設置。 如果安全參數設置,cookie可以通過HTTPs傳遞,例如:
```
$r=setcookie("SECSESSID","1203j01j0s1209jw0s21jxd01h029y779g724jahsa9opk123973",time()+60*60*24*7 /*a week*/,"/","owasp.org",true,true);
if (!$r) die("Could not set session cookie.");
```
#### 瀏覽器相關
很多瀏覽器都有cookies的問題,大部分可以通過設置超時時間為0來解決。
## 認證
### Remember me
很多瀏覽器在Remember me特性上存在風險。通常的方式是生成一次性的token,存儲在用戶cookie中,另外在服務端也要存儲用于校驗。這個token應該和用戶名密碼沒有任何關系,一個長隨機數是推薦的做法。
如果能夠做到防止蠻力破解Remember me的token,增加鎖定措施,是更好的做法。
* 不要在cookie中存儲和用戶名、密碼相關的任何數據
# 配置和部署
配置請參見<a href="https://www.owasp.org/index.php/PHP_Configuration_Cheat_Sheet">PHP配置安全手冊</a>
# 來源相關
本博客來源于對OWSAP的<a href ="https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet">PHP Security Cheat Sheet</a>的翻譯,翻譯權所有,轉載請注明。
其它安全相關
* <a href="https://www.owasp.org/index.php/OWASP_Cheat_Sheet_Series">OWASP Cheat Sheet Series</a>