PHP安全編程最佳實踐

前言

大家都知道由于PHP其簡單、快速、方便、易于開發(fā)且專注于Web領(lǐng)域等特性,使得PHP在目前的Web后端依舊稱霸。但是,因為其動態(tài)特性等一系列因素使得我們在編程時依舊犯下錯誤,最后導(dǎo)致bug的產(chǎn)生。
而我最近在看stackoverflow時,無意中發(fā)現(xiàn)關(guān)于PHP安全編程的相關(guān)知識。因此,將其記錄在博客中便于日后查找。

安全編程

PHP版本號泄露

  • 問題
image.png

如上圖所示,一般默認我們在向服務(wù)器請求數(shù)據(jù)時,返回的響應(yīng)頭中 “X-Powered-By”顯示了我們服務(wù)器使用的PHP版本。這其實是不安全的,因為PHP存在時間已經(jīng)有20多年了每個版本都或多或少都有些bug。如果NB的黑客知道你的PHP的版本號,極有可能利用這些bug攻擊我們的電腦。

  • 解決方案

① 在php.ini配置文件中,設(shè)置expose_php = off
② 在PHP代碼中直接使用 header("X-Powered-By: Magic"); 修改X-Powered-By內(nèi)容
③ 在PHP代碼中直接使用 header_remove("X-Powered-By");

XSS攻擊

  • 問題

XSS攻擊產(chǎn)生的原因在于服務(wù)器沒有對客戶端上傳的數(shù)據(jù)進行校驗,導(dǎo)致客戶端上傳一段js代碼上來。最后,導(dǎo)致在我們渲染頁面的時候,將該js代碼也渲染到網(wǎng)頁上從而產(chǎn)生意外。

如下面代碼所示:

image.png

前端代碼index.html,后臺代碼b.php。當(dāng)我們從瀏覽器打開index.html后點擊提交,b.php直接從POST請求獲取了數(shù)據(jù)并直接輸出到頁面,結(jié)果就是頁面直接輸出js代碼。想象一下黑客不是彈出hello world,而是無限循環(huán)或者直接將頁面元素全部刪除后果是咋樣。

image.png
  • 解決方案
<?php

// 使用htmlspecialchars將字符串中html標簽進行處理
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// 獲取使用下面的方式 當(dāng)然使用htmlspecialchars更加方便
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

// 而且對于動態(tài)生成的URL,也可以使用如下方式來保證安全
<?php
// 使用urlencode來編碼URL
$input = urlencode($_GET['input']);
// 或者使用如下方式
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a  . $input . '">Link</a>';

而且也開始使用第三方庫htmlpurifier來過濾HTML代碼

CSRF攻擊

  • 問題

    CSRF攻擊原理在于使用了用戶瀏覽器上的cookie。我們在使用網(wǎng)銀時會先登錄,然后本地瀏覽器會產(chǎn)生cookie用來標識用戶,之后我們的每一次向服務(wù)器請求都會攜帶該cookie而服務(wù)器也是根據(jù)該cookie判斷用戶是否有權(quán)限進行操作。比如說:銀行有一個轉(zhuǎn)賬請求URL: https://www.example.com?account=xiaoming&money=2000&target=xiaofeng,正常情況下用戶發(fā)出該請求后 服務(wù)器在校驗cookie后通過該請求完成轉(zhuǎn)賬。
    但是,黑客也可能是該網(wǎng)銀的用戶也知道有這條轉(zhuǎn)賬的請求,因此,寫了一個頁面 里面有一個鏈接 <a >,然后,黑客誘導(dǎo)用戶進入該網(wǎng)頁并點擊了該鏈接,如果用戶剛剛已經(jīng)登錄網(wǎng)銀而cookie還在有效期,則在用戶點擊該鏈接后瀏覽器會攜帶有效cookie并向服務(wù)器進行轉(zhuǎn)賬操作,服務(wù)器看到這個cookie認為是用戶本人在進行轉(zhuǎn)賬操作而將20000元轉(zhuǎn)給heike。因此,在用戶不知道的情況完成了一個轉(zhuǎn)賬給heike的合法操作。

  • 解決方案

<form method="get" action="/delete.php">
  <input type="text" name="accnt" placeholder="accnt number" />
  <input type="hidden" name="csrftoken" value="<randomToken>" />
  <input type="submit" />
</form>

如上所示,每一次瀏覽器向服務(wù)器請求頁面時服務(wù)器會動態(tài)生成csrftoken放入表單中,然后瀏覽器向服務(wù)器發(fā)送請求都攜帶csrftoken。這樣服務(wù)器會驗證csrftoken檢查是否是有效請求。
當(dāng)然,你也可以將其使用ajax請求并將csrftoken放入header。獲取你也可以同時檢查HTTP_REFERER以及csrftoken來驗證是否是合理請求

命令行注入

  • 問題

該注入產(chǎn)生的原因還是在于服務(wù)器沒有對客戶端傳入的數(shù)據(jù)進行校驗而直接利用數(shù)據(jù)完成操作,最后導(dǎo)致嚴重的后果。
例如:

<pre>
<?php system('ls ' . $_GET['path']); ?>
</pre>

如果傳入的$_GET['path']字符串是 "; rm -rf /",那么就會執(zhí)行 ls;rm -rf /命令。最后服務(wù)器就會將根目錄的數(shù)據(jù)全部刪除。

  • 解決方案
<pre>
// 使用escapeshellarg或者escapeshellcmd命令對數(shù)據(jù)進行過濾
<?php system('ls ' . escapeshellarg($_GET['path'])); ?>
</pre>

上面執(zhí)行的代碼為 ls '; rm -fr /'

SQL注入

  • 問題

SQL注入產(chǎn)生原因還是在于對客戶端傳入的數(shù)據(jù)沒有進行過濾并直接用來拼接SQL語句,這也是以前PHP產(chǎn)生初期黑客攻擊的常用方式。

  • 解決方案

1) 對客戶端的數(shù)據(jù)進行檢查,過濾一些字符
2) 使用PDO進行預(yù)編譯來綁定參數(shù),從而查詢數(shù)據(jù)庫。目前,如果使用PHP框架也很多針對SQL的解決方案,例如:Yii2框架中AR查詢·

error_reporting

  • 問題

在我們編寫PHP代碼中經(jīng)常會在瀏覽器出現(xiàn) 狀態(tài)碼500 (不要跟我說你從來沒有遇到過),如果PHP配置中打開了展示錯誤提示的功能,那么一些服務(wù)器的錯誤信息會顯示在瀏覽器上。這樣會讓人知道服務(wù)器的相關(guān)信息,那么怎樣讓這些錯誤信息不要顯示在瀏覽器上而輸出到服務(wù)器特定位置,而這就需要用到set_error_handler函數(shù)。

  • 解決方案

① 若沒有打開PHP錯誤提示的功能,可寫如下代碼臨時打開

ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);

② 根據(jù)自己的業(yè)務(wù)要求,將下面的代碼修改后放入自己的PHP代碼中,下面代碼的意思是PHP發(fā)送錯誤時會調(diào)用該函數(shù),然后我通過實現(xiàn)該函數(shù)就可以將錯誤信息重定向到特定位置。

set_error_handler(function($errno , $errstr, $errfile, $errline){
  try{
    $pdo = new PDO("mysql:host=hostname;dbname=databasename", 'dbuser', 'dbpwd', [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]);

    if($stmt = $pdo->prepare("INSERT INTO `errors` (no,msg,file,line) VALUES (?,?,?,?)")){
      if(!$stmt->execute([$errno, $errstr, $errfile, $errline])){
        throw new Exception('Unable to execute query');
      }
    } else {
      throw new Exception('Unable to prepare query');
    }
  } catch (Exception $e){
    error_log('Exception: ' . $e->getMessage() . PHP_EOL . "$errfile:$errline:$errno | $errstr");
  }
});

其他的還有遠程文件包含以及上傳文件攻擊,大家可以查看https://stackoverflow.com/documentation/php/2781/security#t=201708170948498596789了解,它們的攻擊原因都在于對瀏覽器傳入的數(shù)據(jù)么有進行過濾。

總結(jié)

黑客攻擊手段多種多樣,但黑客之所以能夠攻擊到服務(wù)器在于服務(wù)器對于客戶端傳入的數(shù)據(jù)沒有進行校驗。因此,要保證服務(wù)器的安全性必須要防御性編程思想,特別是那些涉及敏感數(shù)據(jù)要多多思考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容