安全領(lǐng)域的最佳實(shí)踐(Best Practices)

2018-02-24 15:40 更新

最佳安全實(shí)踐

下面,我們將會(huì)回顧常見(jiàn)的安全原則,并介紹在使用 Yii 開(kāi)發(fā)應(yīng)用程序時(shí),如何避免潛在安全威脅。

基本準(zhǔn)則

無(wú)論是開(kāi)發(fā)何種應(yīng)用程序,我們都有兩條基本的安全準(zhǔn)則:

  1. 過(guò)濾輸入
  2. 轉(zhuǎn)義輸出

過(guò)濾輸入

過(guò)濾輸入的意思是,用戶輸入不應(yīng)該認(rèn)為是安全的,你需要總是驗(yàn)證你獲得的輸入值是在允許范圍內(nèi)。比如,我們假設(shè) sorting 只能指定為?title,?created_at?和?status?三個(gè)值,然后,這個(gè)值是由用戶輸入提供的,那么,最好在我們接收參數(shù)的時(shí)候,檢查一下這個(gè)值是否是指定的范圍。 對(duì)于基本的 PHP 而言,上述做法類似如下:

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

在 Yii 中,很大可能性,你會(huì)使用?表單校驗(yàn)器?來(lái)執(zhí)行類似的檢查。

轉(zhuǎn)義輸出

轉(zhuǎn)義輸出的意思是,根據(jù)我們使用數(shù)據(jù)的上下文環(huán)境,數(shù)據(jù)需要被轉(zhuǎn)義。比如:在 HTML 上下文,你需要轉(zhuǎn)義?<>?之類的特殊字符。在 JavaScript 或者 SQL 中,也有其他的特殊含義的字符串需要被轉(zhuǎn)義。 由于手動(dòng)的給所用的輸出轉(zhuǎn)義容易出錯(cuò),Yii 提供了大量的工具來(lái)在不同的上下文執(zhí)行轉(zhuǎn)義。

避免 SQL 注入

SQL 注入發(fā)生在查詢語(yǔ)句是由連接未轉(zhuǎn)義的字符串生成的場(chǎng)景,比如:

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

除了提供正確的用戶名外,攻擊者可以給你的應(yīng)用程序輸入類似 '; DROP TABLE user; --` 的語(yǔ)句。 這將會(huì)導(dǎo)致生成如下的 SQL :

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

這是一個(gè)合法的查詢語(yǔ)句,并將會(huì)執(zhí)行以空的用戶名搜索用戶操作,然后,刪除?user?表。這極有可能導(dǎo)致網(wǎng)站出差,數(shù)據(jù)丟失。(你是否進(jìn)行了規(guī)律的數(shù)據(jù)備份?)

在 Yii 中,大部分的數(shù)據(jù)查詢是通過(guò)?Active Record?進(jìn)行的,而其是完全使用 PDO 預(yù)處理語(yǔ)句執(zhí)行 SQL 查詢的。在預(yù)處理語(yǔ)句中,上述示例中,構(gòu)造 SQL 查詢的場(chǎng)景是不可能發(fā)生的。

有時(shí),你仍需要使用?raw queries?或者?query builder。在這種情況下,你應(yīng)該使用安全的方式傳遞參數(shù)。如果數(shù)據(jù)是提供給表列的值,最好使用預(yù)處理語(yǔ)句:

// 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();

如果數(shù)據(jù)是用于指定列的名字,或者表的名字,最好的方式是只允許預(yù)定義的枚舉值。

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

    // ...
}

如果上述方法不行,表名或者列名應(yīng)該被轉(zhuǎn)義。 Yii 針對(duì)這種轉(zhuǎn)義提供了一個(gè)特殊的語(yǔ)法,這樣可以在所有支持的數(shù)據(jù)庫(kù)都使用一套方案。

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

你可以在?Quoting Table and Column Names?中獲取更多的語(yǔ)法細(xì)節(jié)。

防止 XSS 攻擊

XSS 或者跨站腳本發(fā)生在輸出 HTML 到瀏覽器時(shí),輸出內(nèi)容沒(méi)有正確的轉(zhuǎn)義。例如,如果用戶可以輸入其名稱,那么他輸入<script>alert('Hello!');</script>?而非其名字?Alexander,所有輸出沒(méi)有轉(zhuǎn)義直接輸出用戶名的頁(yè)面都會(huì)執(zhí)行 JavaScript 代碼?alert('Hello!');,這會(huì)導(dǎo)致瀏覽器頁(yè)面上出現(xiàn)一個(gè)警告彈出框。就具體的站點(diǎn)而言,除了這種無(wú)意義的警告輸出外,這樣的腳本可以以你的名義發(fā)送一些消息到后臺(tái),甚至執(zhí)行一些銀行交易行為。

避免 XSS 攻擊在 Yii 中非常簡(jiǎn)單,有如下兩種一般情況:

  1. 你希望數(shù)據(jù)以純文本輸出。
  2. 你希望數(shù)據(jù)以 HTML 形式輸出。

如果你需要的是純文本,你可以如下簡(jiǎn)單的轉(zhuǎn)義:

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

如果是 HTML ,我們可以用 HtmlPurifier 幫助類來(lái)執(zhí)行:

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

注意: HtmlPurifier 幫助類的處理過(guò)程較為費(fèi)時(shí),建議增加緩存:

防止 CSRF 攻擊

CSRF 是跨站請(qǐng)求偽造的縮寫。這個(gè)攻擊思想源自許多應(yīng)用程序假設(shè)來(lái)自用戶的瀏覽器請(qǐng)求是由用戶自己產(chǎn)生的,而事實(shí)并非如此。

比如說(shuō):an.example.com?站點(diǎn)有一個(gè)?/logout?URL,當(dāng)以 GET 請(qǐng)求訪問(wèn)時(shí),登出用戶。如果它是由用戶自己操作的,那么一切都沒(méi)有問(wèn)題。但是,有一天壞人在一個(gè)用戶經(jīng)常訪問(wèn)的論壇發(fā)了一個(gè)?<img src="https://atts.w3cschool.cn/attachments/image/cimg/code>?內(nèi)容的帖子。瀏覽器無(wú)法辨別請(qǐng)求一個(gè)圖片還是一個(gè)頁(yè)面,所以,當(dāng)用戶打開(kāi)含有上述標(biāo)簽的頁(yè)面時(shí),他將會(huì)從?an.example.com?登出。

上面就是最原始的思想。有人可能會(huì)說(shuō),登出用戶也不是什么嚴(yán)重問(wèn)題,然而,我們發(fā)送一些 POST 數(shù)據(jù)其實(shí)也不是很麻煩的事情。

為了避免 CSRF 攻擊,你總是需要:

  1. 遵循 HTTP 準(zhǔn)則,比如 GET 不應(yīng)該改變應(yīng)用的狀態(tài)。
  2. 保證 Yii CSRF 保護(hù)開(kāi)啟。

防止文件暴露

默認(rèn)的服務(wù)器 webroot 目錄指向包含有?index.php?的?web?目錄。在共享托管環(huán)境下,這樣是不可能的,這樣導(dǎo)致了所有的代碼,配置,日志都在webroot目錄。

如果是這樣,別忘了拒絕除了?web?目錄以外的目錄的訪問(wèn)權(quán)限。如果沒(méi)法這樣做,考慮將你的應(yīng)用程序托管在其他地方。

在生產(chǎn)環(huán)境關(guān)閉調(diào)試信息和工具

在調(diào)試模式下, Yii 展示了大量的錯(cuò)誤信息,這樣是對(duì)開(kāi)發(fā)有用的。同樣,這些調(diào)試信息對(duì)于攻擊者而言也是方便其用于破解數(shù)據(jù)結(jié)構(gòu),配置值,以及你的部分代碼。永遠(yuǎn)不要在生產(chǎn)模式下將你的?index.php?中的?YII_DEBUG?設(shè)置為?true。

你同樣也不應(yīng)該在生產(chǎn)模式下開(kāi)啟 Gii。它可以被用于獲取數(shù)據(jù)結(jié)構(gòu)信息,代碼,以及簡(jiǎn)單的用 Gii 生成的代碼覆蓋你的代碼。

調(diào)試工具欄同樣也應(yīng)該避免在生產(chǎn)環(huán)境出現(xiàn),除非非常有必要。它將會(huì)暴露所有的應(yīng)用和配置的詳情信息。如果你確定需要,反復(fù)確認(rèn)其訪問(wèn)權(quán)限限定在你自己的 IP。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)