歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> PHP 安全編程建議

PHP 安全編程建議

日期:2017/3/1 9:27:16   编辑:Linux編程

簡介

要提供互聯網服務,當你在開發代碼的時候必須時刻保持安全意識。可能大部分 PHP 腳本都對安全問題都不在意,這很大程度上是因為有大量的無經驗程序員在使用這門語言。但是,沒有理由讓你因為對你的代碼的不確定性而導致不一致的安全策略。當你在服務器上放任何涉及到錢的東西時,就有可能會有人嘗試破解它。創建一個論壇程序或者任何形式的購物車,被攻擊的可能性就上升到了無窮大。

PHP 7 ,你值得擁有 http://www.linuxidc.com/Linux/2015-06/118847.htm

在 CentOS 7.x / Fedora 21 上面體驗 PHP 7.0 http://www.linuxidc.com/Linux/2015-05/117960.htm

CentOS 6.3 安裝LNMP (PHP 5.4,MyySQL5.6) http://www.linuxidc.com/Linux/2013-04/82069.htm

在部署LNMP的時候遇到Nginx啟動失敗的2個問題 http://www.linuxidc.com/Linux/2013-03/81120.htm

Ubuntu安裝Nginx php5-fpm MySQL(LNMP環境搭建) http://www.linuxidc.com/Linux/2012-10/72458.htm

《細說PHP》高清掃描PDF+光盤源碼+全套教學視頻 http://www.linuxidc.com/Linux/2014-03/97536.htm

CentOS 6中配置PHP的LNMP的開發環境 http://www.linuxidc.com/Linux/2013-12/93869.htm

背景

為了確保你的 web 內容安全,這裡有一些常規的安全准則:

別相信表單

攻擊表單很簡單。通過使用一個簡單的 JavaScript 技巧,你可以限制你的表單只允許在評分域中填寫 1 到 5 的數字。如果有人關閉了他們浏覽器的 JavaScript 功能或者提交自定義的表單數據,你客戶端的驗證就失敗了。

用戶主要通過表單參數和你的腳本交互,因此他們是最大的安全風險。你應該學到什麼呢?在 PHP 腳本中,總是要驗證 傳遞給任何 PHP 腳本的數據。在本文中,我們向你演示了如何分析和防范跨站腳本(XSS)攻擊,它可能會劫持用戶憑據(甚至更嚴重)。你也會看到如何防止會玷污或毀壞你數據的 MySQL 注入攻擊。

別相信用戶

假定你網站獲取的每一份數據都充滿了有害的代碼。清理每一部分,即便你相信沒有人會嘗試攻擊你的站點。

關閉全局變量

你可能會有的最大安全漏洞是啟用了 register_globals 配置參數。幸運的是,PHP 4.2 及以後版本默認關閉了這個配置。如果打開了 register_globals,你可以在你的 php.ini 文件中通過改變 register_globals 變量為 Off 關閉該功能:

  1. register_globals =Off

新手程序員覺得注冊全局變量很方便,但他們不會意識到這個設置有多麼危險。一個啟用了全局變量的服務器會自動為全局變量賦任何形式的參數。為了了解它如何工作以及為什麼有危險,讓我們來看一個例子。

假設你有一個稱為 process.php 的腳本,它會向你的數據庫插入表單數據。初始的表單像下面這樣:

  1. <inputname="username"type="text"size="15"maxlength="64">

運行 process.php 的時候,啟用了注冊全局變量的 PHP 會將該參數賦值到 $username 變量。這會比通過 $_POST['username']$_GET['username'] 訪問它節省擊鍵次數。不幸的是,這也會給你留下安全問題,因為 PHP 會設置該變量的值為通過 GET 或 POST 的參數發送到腳本的任何值,如果你沒有顯示地初始化該變量並且你不希望任何人去操作它,這就會有一個大問題。

看下面的腳本,假如 $authorized 變量的值為 true,它會給用戶顯示通過驗證的數據。正常情況下,只有當用戶正確通過了這個假想的 authenticated_user() 函數驗證,$authorized 變量的值才會被設置為真。但是如果你啟用了 register_globals,任何人都可以發送一個 GET 參數,例如 authorized=1 去覆蓋它:

  1. <?php
  2. // Define $authorized = true only if user is authenticated
  3. if(authenticated_user()){
  4. $authorized =true;
  5. }
  6. ?>

這個故事的寓意是,你應該從預定義的服務器變量中獲取表單數據。所有通過 post 表單傳遞到你 web 頁面的數據都會自動保存到一個稱為 $_POST 的大數組中,所有的 GET 數據都保存在 $_GET 大數組中。文件上傳信息保存在一個稱為 $_FILES 的特殊數據中。另外,還有一個稱為 $_REQUEST 的復合變量。

要從一個 POST 方法表單中訪問 username 字段,可以使用 $_POST['username']。如果 username 在 URL 中就使用 $_GET['username']。如果你不確定值來自哪裡,用 $_REQUEST['username']

  1. <?php
  2. $post_value = $_POST['post_value'];
  3. $get_value = $_GET['get_value'];
  4. $some_variable = $_REQUEST['some_value'];
  5. ?>

$_REQUEST 是 $_GET、$_POST、和 $_COOKIE 數組的結合。如果你有兩個或多個值有相同的參數名稱,注意 PHP 會使用哪個。默認的順序是 cookie、POST、然後是 GET。

推薦安全配置選項

這裡有幾個會影響安全功能的 PHP 配置設置。下面是一些顯然應該用於生產服務器的:

  • register_globals 設置為 off
  • safe_mode 設置為 off
  • error_reporting 設置為 off。如果出現錯誤了,這會向用戶浏覽器發送可見的錯誤報告信息。對於生產服務器,使用錯誤日志代替。開發服務器如果在防火牆後面就可以啟用錯誤日志。(LCTT 譯注:此處據原文邏輯和常識,應該是“開發服務器如果在防火牆後面就可以啟用錯誤報告,即 on。”)
  • 停用這些函數:system()、exec()、passthru()、shell_exec()、proc_open()、和 popen()。
  • open_basedir 為 /tmp(以便保存會話信息)目錄和 web 根目錄,以便腳本不能訪問這些選定區域外的文件。
  • expose_php 設置為 off。該功能會向 Apache 頭添加包含版本號的 PHP 簽名。
  • allow_url_fopen 設置為 off。如果你能夠注意你代碼中訪問文件的方式-也就是你驗證所有輸入參數,這並不嚴格需要。
  • allow_url_include 設置為 off。對於任何人來說,實在沒有明智的理由會想要訪問通過 HTTP 包含的文件。

一般來說,如果你發現想要使用這些功能的代碼,你就不應該相信它。尤其要小心會使用類似 system() 函數的代碼-它幾乎肯定有缺陷。

啟用了這些設置後,讓我們來看看一些特定的攻擊以及能幫助你保護你服務器的方法。

SQL 注入攻擊

由於 PHP 傳遞到 MySQL 數據庫的查詢語句是用強大的 SQL 編程語言編寫的,就有了某些人通過在 web 查詢參數中使用 MySQL 語句嘗試 SQL 注入攻擊的風險。通過在參數中插入有害的 SQL 代碼片段,攻擊者會嘗試進入(或破壞)你的服務器。

假如說你有一個最終會放入變量 $product 的表單參數,你使用了類似下面的 SQL 語句:

  1. $sql ="select * from pinfo where product = '$product'";

如果參數是直接從表單中獲得的,應該使用 PHP 自帶的數據庫特定轉義函數,類似:

  1. $sql ='Select * from pinfo where product = '"'
  2. mysql_real_escape_string($product) . '"';

如果不這樣做的話,有人也許會把下面的代碼段放到表單參數中:

  1. 39'; DROP pinfo; SELECT 'FOO

那麼 $sql 的結果就是:

  1. select product from pinfo where product ='39'; DROP pinfo; SELECT 'FOO'

由於分號是 MySQL 的語句分隔符,數據庫會運行下面三條語句:

  1. select*from pinfo where product ='39'
  2. DROP pinfo
  3. SELECT 'FOO'

好了,你丟失了你的表。

注意實際上 PHP 和 MySQL 不會運行這種特殊語法,因為 mysql_query() 函數只允許每個請求處理一個語句。但是,一個子查詢仍然會生效。

要防止 SQL 注入攻擊,做這兩件事:

  • 總是驗證所有參數。例如,如果需要一個數字,就要確保它是一個數字。
  • 總是對數據使用 mysql_real_escape_string() 函數轉義數據中的任何引號和雙引號。

注意:要自動轉義任何表單數據,可以啟用魔術引號(Magic Quotes)。

一些 MySQL 破壞可以通過限制 MySQL 用戶權限避免。任何 MySQL 賬戶可以限制為只允許對選定的表進行特定類型的查詢。例如,你可以創建只能選擇行的 MySQL 用戶。但是,這對於動態數據並不十分有用,另外,如果你有敏感的用戶信息,可能某些人能訪問其中一些數據,但你並不希望如此。例如,一個訪問賬戶數據的用戶可能會嘗試注入訪問另一個人的賬戶號碼的代碼,而不是為當前會話指定的號碼。

防止基本的 XSS 攻擊

XSS 表示跨站腳本。不像大部分攻擊,該漏洞發生在客戶端。XSS 最常見的基本形式是在用戶提交的內容中放入 JavaScript 以便偷取用戶 cookie 中的數據。由於大部分站點使用 cookie 和 session 驗證訪客,偷取的數據可用於模擬該用戶-如果是一個常見的用戶賬戶就會深受麻煩,如果是管理員賬戶甚至是徹底的慘敗。如果你不在站點中使用 cookie 和 session ID,你的用戶就不容易被攻擊,但你仍然應該明白這種攻擊是如何工作的。

不像 MySQL 注入攻擊,XSS 攻擊很難預防。Yahoo、eBay、Apple、以及 Microsoft 都曾經受 XSS 影響。盡管攻擊不包含 PHP,但你可以使用 PHP 來剝離用戶數據以防止攻擊。為了防止 XSS 攻擊,你應該限制和過濾用戶提交給你站點的數據。正是因為這個原因,大部分在線公告板都不允許在提交的數據中使用 HTML 標簽,而是用自定義的標簽格式代替,例如 [b][linkto]

讓我們來看一個如何防止這類攻擊的簡單腳本。對於更完善的解決辦法,可以使用 SafeHTML,本文的後面部分會討論到。

  1. function transform_HTML($string, $length =null){
  2. // Helps prevent XSS attacks
  3. // Remove dead space.
  4. $string = trim($string);
  5. // Prevent potential Unicode codec problems.
  6. $string = utf8_decode($string);
  7. // HTMLize HTML-specific characters.
  8. $string = htmlentities($string, ENT_NOQUOTES);
  9. $string = str_replace("#","&#35;", $string);
  10. $string = str_replace("%","&#37;", $string);
  11. $length = intval($length);
  12. if($length >0){
  13. $string = substr($string,0, $length);
  14. }
  15. return $string;
  16. }

這個函數將 HTML 特定的字符轉換為 HTML 字面字符。一個浏覽器對任何通過這個腳本的 HTML 以非標記的文本呈現。例如,考慮下面的 HTML 字符串:

  1. <STRONG>Bold Text</STRONG>

一般情況下,HTML 會顯示為:Bold Text

但是,通過 transform_HTML() 後,它就像原始輸入一樣呈現。原因是處理的字符串中的標簽字符串轉換為 HTML 實體。transform_HTML() 的結果字符串的純文本看起來像下面這樣:

  1. <STRONG>Bold Text</STRONG>

該函數的實質是 htmlentities() 函數調用,它會將 <、>、和 & 轉換為 &lt;&gt;、和 &amp;。盡管這會處理大部分的普通攻擊,但有經驗的 XSS 攻擊者有另一種把戲:用十六進制或 UTF-8 編碼惡意腳本,而不是采用普通的 ASCII 文本,從而希望能繞過你的過濾器。他們可以在 URL 的 GET 變量中發送代碼,告訴浏覽器,“這是十六進制代碼,你能幫我運行嗎?” 一個十六進制例子看起來像這樣:

  1. <ahref="http://host/a.php?variable=%22%3e %3c%53%43%52%49%50%54%3e%44%6f%73%6f%6d%65%74%68%69%6e%67%6d%61%6c%69%63%69%6f%75%73%3c%2f%53%43%52%49%50%54%3e">

浏覽器渲染這個信息的時候,結果就是:

  1. <ahref="http://host/a.php?variable="><SCRIPT>Dosomethingmalicious</SCRIPT>

為了防止這種情況,transform_HTML() 采用額外的步驟把 # 和 % 符號轉換為它們的實體,從而避免十六進制攻擊,並轉換 UTF-8 編碼的數據。

最後,為了防止某些人用很長的輸入超載字符串從而導致某些東西崩潰,你可以添加一個可選的 $length 參數來截取你指定最大長度的字符串。

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2015-07/120356p2.htm

Copyright © Linux教程網 All Rights Reserved