Yii2 的安全最佳實踐(Best Practices Of Security)

下面,咱們將會回顧常見的安全原則,並介紹在使用 Yii 開發應用程序時,如何避免潛在安全威脅。 大多數這些原則並不是您獨有,而是適用於網站或軟件開發, 所以,您還能夠找到有關這些背後的通常概念的進一步閱讀的連接。php

基本準則

不管是開發何種應用程序,咱們都有兩條基本的安全準則:html

  • 過濾輸入
  • 轉義輸出

過濾輸入

過濾輸入的意思是,用戶輸入不該該認爲是安全的,你須要老是驗證你得到的輸入值是在容許範圍內。 好比,咱們假設能夠經過三個字段完成排序 title,created_at 和 status,而後,這個值是由用戶輸入提供的, 那麼,最好在咱們接收參數的時候,檢查一下這個值是不是指定的範圍。 對於基本的 PHP 而言,上述作法相似以下:nginx

$sortBy = $_GET['sort'];
if (!in_array($sortBy, ['title', 'created_at', 'status'])) {
    throw new Exception('Invalid sort value.');
}

在 Yii 中,很大可能性,你會使用 表單校驗器 來執行相似的檢查。web

進一步閱讀該主題:sql

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...數據庫

轉義輸出

轉義輸出的意思是,根據咱們使用數據的上下文環境,數據須要被轉義。好比:在 HTML 上下文, 你須要轉義 <,> 之類的特殊字符。在 JavaScript 或者 SQL 中,也有其餘的特殊含義的字符串須要被轉義。 因爲手動的給所用的輸出轉義容易出錯, Yii 提供了大量的工具來在不一樣的上下文執行轉義。apache

進一步閱讀該話題:瀏覽器

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...
https://www.owasp.org/index.p...緩存

避免 SQL 注入

SQL 注入發生在查詢語句是由鏈接未轉義的字符串生成的場景,好比:安全

$username = $_GET['username'];
$sql = "SELECT * FROM user WHERE username = '$username'";

除了提供正確的用戶名外,攻擊者能夠給你的應用程序輸入相似 '; DROP TABLE user; -- 的語句。 這將會致使生成以下的 SQL:

SELECT * FROM user WHERE username = ''; DROP TABLE user; --'

這是一個合法的查詢語句,並將會執行以空的用戶名搜索用戶操做,而後,刪除 user 表。 這極有可能致使網站出錯,數據丟失。(你是否進行了規律的數據備份?)

在 Yii 中,大部分的數據查詢是經過 Active Record 進行的, 而其是徹底使用 PDO 預處理語句執行 SQL 查詢的。在預處理語句中,上述示例中,構造 SQL 查詢的場景是不可能發生的。

有時,你仍須要使用 raw queries 或者 query builder。 在這種狀況下,你應該使用安全的方式傳遞參數。若是數據是提供給表列的值,最好使用預處理語句:

// query builder
$userIDs = (new Query())
    ->select('id')
    ->from('user')
    ->where('status=:status', [':status' => $status])
    ->all();

// DAO
$userIDs = $connection
    ->createCommand('SELECT id FROM user where status=:status')
    ->bindValues([':status' => $status])
    ->queryColumn();

若是數據是用於指定列的名字,或者表的名字,最好的方式是隻容許預約義的枚舉值。

function actionList($orderBy = null)
{
    if (!in_array($orderBy, ['name', 'status'])) {
        throw new BadRequestHttpException('Only name and status are allowed to order by.')
    }

    // ...
}

若是上述方法不行,表名或者列名應該被轉義。Yii 針對這種轉義提供了一個特殊的語法, 這樣能夠在全部支持的數據庫都使用一套方案。

$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

你能夠在 Quoting Table and Column Names 中獲取更多的語法細節。

進一步閱讀該話題:

https://www.owasp.org/index.p...

防止 XSS 攻擊

XSS 或者跨站腳本發生在輸出 HTML 到瀏覽器時,輸出內容沒有正確的轉義。 例如,若是用戶能夠輸入其名稱,那麼他輸入 <script>alert('Hello!');</script> 而非其名字 Alexander, 全部輸出沒有轉義直接輸出用戶名的頁面都會執行 JavaScript 代碼 alert('Hello!');, 這會致使瀏覽器頁面上出現一個警告彈出框。就具體的站點而言,除了這種無心義的警告輸出外, 這樣的腳本能夠以你的名義發送一些消息到後臺,甚至執行一些銀行交易行爲。

避免 XSS 攻擊在 Yii 中很是簡單,有以下兩種通常狀況:

  • 你但願數據以純文本輸出。
  • 你但願數據以 HTML 形式輸出。

若是你須要的是純文本,你能夠以下簡單的轉義:

<?= \yii\helpers\Html::encode($username) ?>

若是是 HTML,咱們能夠用 HtmlPurifier 幫助類來執行:

<?= \yii\helpers\HtmlPurifier::process($description) ?>

注意 HtmlPurifier 幫助類的處理過程較爲費時,建議增長緩存。

進一步閱讀該話題:

https://www.owasp.org/index.p...

防止 CSRF 攻擊

CSRF 是跨站請求僞造的縮寫。這個攻擊思想源自許多應用程序假設來自用戶的瀏覽器請求是由用戶本身產生的, 而事實並不是如此。

例如,網站 an.example.com 有一個 /logout 網址,當使用簡單的 GET 請求訪問時, 記錄用戶退出。 只要用戶的請求一切正常,可是有一天壞人們故意在用戶常常訪問的論壇上放上 <img src="http://an.example.com/logout">。 瀏覽器在請求圖像或請求頁面之間沒有任何區別, 因此當用戶打開一個帶有這樣一個被操做過的 <img> 標籤的頁面時, 瀏覽器將 GET 請求發送到該 URL,用戶將從 an.example.com 註銷。

這是 CSRF 攻擊如何運做的基本思路。能夠說用戶退出並非一件嚴重的事情, 然而這僅僅是一個例子,使用這種方法能夠作更多的事情,例如觸發付款或者是改變數據。 想象一下若是某個網站有一個這樣的 http://an.example.com/purse/transfer?to=anotherUser&amount=2000 網址。 使用 GET 請求訪問它會致使從受權用戶帳戶轉帳 $2000 給 anotherUser。 咱們知道,瀏覽器將始終發送 GET 請求來加載圖像, 因此咱們能夠修改代碼以僅接受該 URL 上的 POST 請求。 不幸的是,這並不會拯救咱們,由於攻擊者能夠放置一些 JavaScript 代碼而不是 <img> 標籤,這樣就能夠向該 URL 發送 POST 請求。

出於這個緣由,Yii 應用其餘機制來防止 CSRF 攻擊。

爲了不 CSRF 攻擊,你老是須要:

  1. 遵循 HTTP 準則,好比 GET 不該該改變應用的狀態。 有關詳細信息,請參閱 RFC2616
  2. 保證 Yii CSRF 保護開啓。

有的時候你須要對每一個控制器和/或方法使用禁用 CSRF。能夠經過設置其屬性來實現:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public $enableCsrfValidation = false;

    public function actionIndex()
    {
        // CSRF validation will not be applied to this and other actions
    }

}

要對每一個自定義方法禁用 CSRF 驗證,您可使用:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public function beforeAction($action)
    {
        // ...set `$this->enableCsrfValidation` here based on some conditions...
        // call parent method that will check CSRF if such property is true.
        return parent::beforeAction($action);
    }
}

在 standalone actions 禁用 CSRF 必須在 init() 方法中設置。 不要把這段代碼放在 beforeRun() 方法中,由於它不會起任何做用。

<?php

namespace app\components;

use yii\base\Action;

class ContactAction extends Action
{
    public function init()
    {
        parent::init();
        $this->controller->enableCsrfValidation = false;
    }

    public function run()
    {
          $model = new ContactForm();
          $request = Yii::$app->request;
          if ($request->referrer === 'yiipowered.com'
              && $model->load($request->post())
              && $model->validate()
          ) {
              $model->sendEmail();
          }
    }
}
警告: 禁用 CSRF 將容許任何站點向您的站點發送 POST 請求。在這種狀況下,實施額外驗證很是重要,例如檢查 IP 地址或祕密令牌。

進一步閱讀該話題:

https://www.owasp.org/index.p...

防止文件暴露

默認的服務器 webroot 目錄指向包含有 index.phpweb 目錄。在共享託管環境下,這樣是不可能的, 這樣致使了全部的代碼,配置,日誌都在webroot目錄。

若是是這樣,別忘了拒絕除了 web 目錄之外的目錄的訪問權限。 若是無法這樣作,考慮將你的應用程序託管在其餘地方。

在生產環境關閉調試信息和工具
在調試模式下, Yii 展現了大量的錯誤信息,這樣是對開發有用的。 一樣,這些調試信息對於攻擊者而言也是方便其用於破解數據結構,配置值,以及你的部分代碼。 永遠不要在生產模式下將你的 index.php 中的 YII_DEBUG 設置爲 true

你一樣也不該該在生產模式下開啓 Gii。它能夠被用於獲取數據結構信息, 代碼,以及簡單的用 Gii 生成的代碼覆蓋你的代碼。

調試工具欄一樣也應該避免在生產環境出現,除非很是有必要。它將會暴露全部的應用和配置的詳情信息。 若是你肯定須要,反覆確認其訪問權限限定在你本身的 IP。

進一步閱讀該話題:

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...

使用 TLS 上的安全鏈接(HTTPS的啓用)

Yii 提供依賴 cookie 和/或 PHP 會話的功能。若是您的鏈接受到威脅,這些可能會很容易受到攻擊。 若是應用程序經過 TLS 使用安全鏈接,則風險會下降。
有關如何配置它的說明,請參閱您的 Web 服務器文檔。

如今各大雲平臺都提供免費的單域名SSL證書,如阿里雲,騰訊雲

以阿里雲部署Nginx下的SSL證書爲例:
修改nginx.conf文件以下:

# 如下屬性中以ssl開頭的屬性表明與證書配置有關,其餘屬性請根據本身的須要進行配置。
server {
    listen 443 ssl;
    server_name localhost;  # localhost修改成您證書綁定的域名。
    
    root html;
    index index.html index.htm;
    ssl_certificate cert/domain name.pem;   #將domain name.pem替換成您證書的文件名。
    ssl_certificate_key cert/domain name.key;   #將domain name.key替換成您證書的密鑰文件名。
    ssl_session_timeout 5m;
    #使用此加密套件。
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 
    #使用該協議進行配置。 
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;   
    ssl_prefer_server_ciphers on;   
    location / {
        root html;   #站點目錄。
        index index.html index.htm;   
    }
}

設置http請求自動跳轉https。(通常都這麼作)

server {
  listen 80;
  server_name www.example.com;
  return 301 https://$server_name$request_uri;
}
參考官方說明,寫的能不能配置成功本身腦補
騰訊雲 Nginx 服務器證書安裝
阿里雲 在Nginx/Tengine服務器上安裝證書

安全服務器配置

本節的目的是強調在爲基於 Yii 的網站提供服務配置時須要考慮的風險。 除了這裏涉及的要點以外, 可能還有其餘與安全相關的配置選項, 因此不要認爲這部分是完整的。

避免 Host-header 攻擊

像 yiiwebUrlManager 和 yiihelpersUrl 這樣的類會使用 currently requested host name 來生成連接。 若是 Web 服務器配置爲獨立於 Host 標頭的值提供相同的站點,這個信息並不可靠, 而且 可能由發送HTTP請求的用戶僞造。 在這種狀況下,您應該修復您的 Web 服務器配置以便僅爲指定的主機名提供站點服務 或者經過設置 request 應用程序組件的 hostInfo 屬性來顯式設置或過濾該值。

注意: 您應該始更傾向於使用 web 服務器配置 'host header attack' 保護而不是使用過濾器。 僅當服務器配置設置不可用時 yiifiltersHostControl 才應該被使用。

以Nginx爲例:

方法一:

修改nginx.conf

添加一個默認server,當host頭被修改匹配不到server時會跳到該默認server,該默認server直接返回403錯誤。

例子以下:

server {

       listen 80 default;

       server_name _;
       server_name_in_redirect off;
       location / {

            return 403;

       }

       }

重啓nginx便可。

方法二:

修改nginx.conf

在目標server添加檢測規則,參考如下標紅配置:

server {

       server_name  abc.com;

       listen       80;

        if ($http_Host !~*^abc.com:80$)

        {
            return 403;
        }

       ...

       }

重啓nginx便可。

有關於服務器配置的更多信息,請參閱您的 web 服務器的文檔:

若是您無權訪問服務器配置,您能夠在應用程序級別設置 yii\filters\HostControl 過濾器, 以防此類的攻擊。

// Web Application configuration file
return [
    'as hostControl' => [
        'class' => 'yii\filters\HostControl',
        'allowedHosts' => [
            'example.com',
            '*.example.com',
        ],
        'fallbackHostInfo' => 'https://example.com',
    ],
    // ...
];

補充閱讀

web安全攻防思惟導圖

  • web攻擊及防護技術 (來源網絡)

web攻擊及防護技術

相關文章
相關標籤/搜索