摘要:認證事件類在登錄和注銷流程引發一些事件。成功注銷后引發。提供兩種授權方法存取控制過濾器和基于角色的存取控制。允許已認證用戶執行操作。指定一個回調函數用于判定該規則是否滿足條件。
簡述
在程序開發過程中,往往都不能忽視安全問題,無論你的框架有多么完美,都會有破綻,所以完善自己的系統,從程序開發的安全角度去思考問題,把一切潛在的危機扼殺在搖籃中。
認證(Authentication)認證是鑒定用戶身份的過程。
它通常使用一個標識符 (如用戶名或電子郵件地址)和一個加密令牌(比如密碼或者存取令牌)來 鑒別用戶身份。
認證是登錄功能的基礎。
Yii提供了一個認證框架,它連接了不同的組件以支持登錄。欲使用這個框架, 你主要需要做以下工作:
設置用戶組件 yiiwebUser ;
創建一個類實現 yiiwebIdentityInterface 接口。
配置 yiiwebUser用戶組件 yiiwebUser 用來管理用戶的認證狀態。
這需要你 指定一個含有實際認證邏輯的認證類 yiiwebUser::identityClass。
在以下web應用的配置項中,將用戶用戶組件 yiiwebUser 的 認證類 yiiwebUser::identityClass 配置成 模型類 appmodelsUser。
return [ "components" => [ "user" => [ "identityClass" => "appmodelsUser", ], ], ];認證接口 yiiwebIdentityInterface 的實現
認證類 yiiwebUser::identityClass 必須實現包含以下方法的 認證接口 yiiwebIdentityInterface:
yiiwebIdentityInterface::findIdentity():根據指定的用戶ID查找 認證模型類的實例,當你需要使用session來維持登錄狀態的時候會用到這個方法。
yiiwebIdentityInterface::findIdentityByAccessToken():根據指定的存取令牌查找 認證模型類的實例,該方法用于 通過單個加密令牌認證用戶的時候(比如無狀態的RESTful應用)。
yiiwebIdentityInterface::getId():獲取該認證實例表示的用戶的ID。
yiiwebIdentityInterface::getAuthKey():獲取基于 cookie 登錄時使用的認證密鑰。 認證密鑰儲存在 cookie 里并且將來會與服務端的版本進行比較以確保 cookie的有效性。
yiiwebIdentityInterface::validateAuthKey() :是基于 cookie 登錄密鑰的 驗證的邏輯的實現。
用不到的方法可以空著,例如,你的項目只是一個 無狀態的 RESTful 應用,只需實現 yiiwebIdentityInterface::findIdentityByAccessToken() 和 yiiwebIdentityInterface::getId() ,其他的方法的函數體留空即可。
下面的例子是一個通過結合了 user 數據表的 AR 模型 Active Record 實現的一個認證類 yiiwebUser::identityClass。
$token]); } /** * @return int|string 當前用戶ID */ public function getId() { return $this->id; } /** * @return string 當前用戶的(cookie)認證密鑰 */ public function getAuthKey() { return $this->auth_key; } /** * @param string $authKey * @return boolean if auth key is valid for current user */ public function validateAuthKey($authKey) { return $this->getAuthKey() === $authKey; } }
如上所述,如果你的應用利用 cookie 登錄, 你只需要實現 getAuthKey() 和 validateAuthKey() 方法。這樣的話,你可以使用下面的代碼在 user 表中生成和存儲每個用戶的認證密鑰。
class User extends ActiveRecord implements IdentityInterface{ ...... public function beforeSave($insert) { if (parent::beforeSave($insert)) { if ($this->isNewRecord) { $this->auth_key = Yii::$app->security->generateRandomString(); } return true; } return false; } }
注意:不要混淆 user 認證類和用戶組件 yiiwebUser。前者是實現 認證邏輯的類,通常用關聯了 持久性存儲的用戶信息的AR模型 Active Record 實現。后者是負責管理用戶認證狀態的 應用組件。
使用用戶組件 yiiwebUser在 user 應用組件方面,你主要用到 yiiwebUser 。
你可以使用表達式 Yii::$app->user->identity 檢測當前用戶身份。它返回 一個表示當前登錄用戶的認證類 yiiwebUser::identityClass 的實例, 未認證用戶(游客)則返回 null。
下面的代碼展示了如何從 yiiwebUser 獲取其他認證相關信息:
// 當前用戶的身份實例。未認證用戶則為 Null 。 $identity = Yii::$app->user->identity; // 當前用戶的ID。 未認證用戶則為 Null 。 $id = Yii::$app->user->id; // 判斷當前用戶是否是游客(未認證的) $isGuest = Yii::$app->user->isGuest;
你可以使用下面的代碼登錄用戶:
// 使用指定用戶名獲取用戶身份實例。 // 請注意,如果需要的話您可能要檢驗密碼 $identity = User::findOne(["username" => $username]); // 登錄用戶 Yii::$app->user->login($identity); yiiwebUser::login() 方法將當前用戶的身份登記到 yiiwebUser。
如果 session 設置為 yiiwebUser::enableSession,則使用 session 記錄用戶身份,用戶的 認證狀態將在整個會話中得以維持。
如果開啟自動登錄 yiiwebUser::enableAutoLogin 則基于 cookie 登錄(如:記住登錄狀態),它將使用 cookie 保存用戶身份,這樣 只要 cookie 有效就可以恢復登錄狀態。
為了使用 cookie 登錄,你需要在應用配置文件中將 yiiwebUser::enableAutoLogin 設為 true。 你還需要在 yiiwebUser::login() 方法中 傳遞有效期(記住登錄狀態的時長)參數。
注銷用戶:
Yii::$app->user->logout();
請注意,啟用 session 時注銷用戶才有意義。該方法將從內存和 session 中 同時清理用戶認證狀態。
默認情況下,它還會注銷所有的 用戶會話數據。
如果你希望保留這些會話數據,可以換成 Yii::$app->user->logout(false) 。認證事件
yiiwebUser 類在登錄和注銷流程引發一些事件。
1、yiiwebUser::EVENT_BEFORE_LOGIN:在登錄 yiiwebUser::login() 時引發。 如果事件句柄將事件對象的 yiiwebUserEvent::isValid 屬性設為 false, 登錄流程將會被取消。 2、yiiwebUser::EVENT_AFTER_LOGIN:登錄成功后引發。 3、yiiwebUser::EVENT_BEFORE_LOGOUT:注銷 yiiwebUser::logout() 前引發。 如果事件句柄將事件對象的 yiiwebUserEvent::isValid 屬性設為 false, 注銷流程將會被取消。 4、yiiwebUser::EVENT_AFTER_LOGOUT:成功注銷后引發。
你可以通過響應這些事件來實現一些類似登錄統計、在線人數統計的功能。例如, 在登錄后 yiiwebUser::EVENT_AFTER_LOGIN 的處理程序,你可以將用戶的登錄時間和IP記錄到 user 表中。
授權(Authorization)授權是指驗證用戶是否允許做某件事的過程。
Yii提供兩種授權方法: 存取控制過濾器(ACF)和基于角色的存取控制(RBAC)。
存取控制過濾器存取控制過濾器(ACF)是一種通過 yiifiltersAccessControl 類來實現的簡單授權方法, 非常適用于僅需要簡單的存取控制的應用。
正如其名稱所指,ACF 是一個種行動(action)過濾器 filter,可在控制器或者模塊中使用。當一個用戶請求一個 action 時, ACF會檢查 yiifiltersAccessControl::rules 列表,判斷該用戶是否允許執 行所請求的action。(譯者注: action 在本文中視情況翻譯為行動、操作、方法等)
下述代碼展示如何在 site 控制器中使用 ACF:
use yiiwebController; use yiifiltersAccessControl; class SiteController extends Controller{ public function behaviors() { return [ "access" => [ "class" => AccessControl::className(), "only" => ["login", "logout", "signup"], "rules" => [ [ "allow" => true, "actions" => ["login", "signup"], "roles" => ["?"], ], [ "allow" => true, "actions" => ["logout"], "roles" => ["@"], ], ], ], ]; } // ... }
上面的代碼中 ACF 以行為 (behavior) 的形式附加到 site 控制器。 這就是很典型的使用行動過濾器的方法。
only 選項指明 ACF 應當只 對 login, logout 和 signup 方法起作用。所有其它的 site 控制器中的方法不受存取控制的限制。
rules 選項列出了 yiifiltersAccessRule,解讀如下:
允許所有訪客(還未經認證的用戶)執行 login 和 signup 操作。 roles 選項包含的問號 ? 是一個特殊的標識,代表”訪客用戶”。
允許已認證用戶執行 logout 操作。@是另一個特殊標識, 代表”已認證用戶”。
ACF 自上向下逐一檢查存取規則,直到找到一個與當前 欲執行的操作相符的規則。 然后該匹配規則中的 allow 選項的值用于判定該用戶是否獲得授權。如果沒有找到匹配的規則, 意味著該用戶沒有獲得授權。(譯者注: only 中沒有列出的操作,將無條件獲得授權)
當 ACF 判定一個用戶沒有獲得執行當前操作的授權時,它的默認處理是:
如果該用戶是訪客,將調用 yiiwebUser::loginRequired() 將用戶的瀏覽器重定向到登錄頁面。
如果該用戶是已認證用戶,將拋出一個 yiiwebForbiddenHttpException 異常。
你可以通過配置 yiifiltersAccessControl::denyCallback 屬性定制該行為:
[ "class" => AccessControl::className(), ... "denyCallback" => function ($rule, $action) { throw new Exception("You are not allowed to access this page"); } ]
yiifiltersAccessRule 支持很多的選項。
下列是所支持選項的總覽:你可以派生 yiifiltersAccessRule 來創建自定義的存取規則類。
* yiifiltersAccessRule::allow: 指定該規則是 "允許" 還是 "拒絕" 。(譯者注:true是允許,false是拒絕) * yiifiltersAccessRule::actions:指定該規則用于匹配哪些操作。 它的值應該是操作方法的ID數組。匹配比較是大小寫敏感的。如果該選項為空,或者不使用該選項, 意味著當前規則適用于所有的操作。 1、yiifiltersAccessRule::controllers:指定該規則用于匹配哪些控制器。 它的值應為控制器ID數組。匹配比較是大小寫敏感的。如果該選項為空,或者不使用該選項, 則意味著當前規則適用于所有的操作。(譯者注:這個選項一般是在控制器的自定義父類中使用才有意義) 2、yiifiltersAccessRule::roles:指定該規則用于匹配哪些用戶角色。 系統自帶兩個特殊的角色,通過 yiiwebUser::isGuest 來判斷: * ?: 用于匹配訪客用戶 (未經認證) * @: 用于匹配已認證用戶 3、使用其他角色名時,將觸發調用 yiiwebUser::can(),這時要求 RBAC 的支持 (在下一節中闡述)。 如果該選項為空或者不使用該選項,意味著該規則適用于所有角色。 4、yiifiltersAccessRule::ips:指定該規則用于匹配哪些 yiiwebRequest::userIP 。 IP 地址可在其末尾包含通配符 * 以匹配一批前綴相同的IP地址。 例如,192.168.* 匹配所有 192.168. 段的IP地址。 如果該選項為空或者不使用該選項,意味著該規則適用于所有角色。 5、yiifiltersAccessRule::verbs:指定該規則用于匹配哪種請求方法(例如GET,POST)。 這里的匹配大小寫不敏感。 6、yiifiltersAccessRule::matchCallback:指定一個PHP回調函數用于 判定該規則是否滿足條件。(譯者注:此處的回調函數是匿名函數) 7、yiifiltersAccessRule::denyCallback: 指定一個PHP回調函數, 當這個規則不滿足條件時該函數會被調用。(譯者注:此處的回調函數是匿名函數)
以下例子展示了如何使用 matchCallback 選項, 可使你設計任意的訪問權限檢查邏輯:
use yiifiltersAccessControl; class SiteController extends Controller{ public function behaviors() { return [ "access" => [ "class" => AccessControl::className(), "only" => ["special-callback"], "rules" => [ [ "actions" => ["special-callback"], "allow" => true, "matchCallback" => function ($rule, $action) { return date("d-m") === "31-10"; } ], ], ], ]; } // 匹配的回調函數被調用了!這個頁面只有每年的10月31號能訪問(譯者注:原文在這里說該方法是回調函數不確切,讀者不要和 `matchCallback` 的值即匿名的回調函數混淆理解)。 public function actionSpecialCallback() { return $this->render("happy-halloween"); } }基于角色的存取控制 (RBAC)
基于角色的存取控制 (RBAC) 提供了一個簡單而強大的集中式存取控制機制。
Yii 實現了通用的分層的 RBAC,遵循的模型是 NIST RBAC model. 它通過 yiirbacManagerInterface application component 提供 RBAC 功能。
使用 RBAC 涉及到兩部分工作。第一部分是建立授權數據, 而第二部分是使用這些授權數據在需要的地方執行檢查。
為方便后面的講述,這里先介紹一些 RBAC 的基本概念。
角色是 權限 的集合 (例如:建貼、改貼)。一個角色 可以指派給一個或者多個用戶。要檢查某用戶是否有一個特定的權限, 系統會檢查該包含該權限的角色是否指派給了該用戶。
可以用一個規則 rule 與一個角色或者權限關聯。一個規則用一段代碼代表, 規則的執行是在檢查一個用戶是否滿足這個角色或者權限時進行的。
例如,"改帖" 的權限 可以使用一個檢查該用戶是否是帖子的創建者的規則。權限檢查中,如果該用戶 不是帖子創建者,那么他(她)將被認為不具有 "改帖"的權限。
角色和權限都可以按層次組織。特定情況下,一個角色可能由其他角色或權限構成, 而權限又由其他的權限構成。
Yii 實現了所謂的 局部順序 的層次結構,包含更多的特定的 樹 的層次。 一個角色可以包含一個權限,反之則不行。(譯者注:可理解為角色在上方,權限在下方,從上到下如果碰到權限那么再往下不能出現角色)
配置 RBAC在開始定義授權數據和執行存取檢查之前,需要先配置應用組件 yiibaseApplication::authManager 。
Yii 提供了兩套授權管理器: yiirbacPhpManager 和 yiirbacDbManager。前者使用 PHP 腳本存放授權數據, 而后者使用數據庫存放授權數據。 如果你的應用不要求大量的動態角色和權限管理, 你可以考慮使用前者。
使用 PhpManager
以下代碼展示使用 yiirbacPhpManager 時如何在應用配置文件中配置 authManager:
return [ // ... "components" => [ "authManager" => [ "class" => "yii bacPhpManager", ], // ... ], ];
現在可以通過 Yii::$app->authManager 訪問 authManager 。
yiirbacPhpManager 默認將 RBAC 數據保存在 @app/rbac 目錄下的文件中。 如果權限層次數據在運行時會被修改,需確保WEB服務器進程對該目錄和其中的文件有寫權限。
以下代碼展示使用 yiirbacDbManager 時如何在應用配置文件中配置 authManager:
return [
// ... "components" => [ "authManager" => [ "class" => "yii bacDbManager", ], // ... ],
];
DbManager 使用4個數據庫表存放它的數據:
yiirbacDbManager::$itemTable: 該表存放授權條目(譯者注:即角色和權限)。默認表名為 "auth_item" 。
yiirbacDbManager::$itemChildTable: 該表存放授權條目的層次關系。默認表名為 "auth_item_child"。
yiirbacDbManager::$assignmentTable: 該表存放授權條目對用戶的指派情況。默認表名為 "auth_assignment"。
yiirbacDbManager::$ruleTable: 該表存放規則。默認表名為 "auth_rule"。
繼續之前,你需要在數據庫中創建這些表。
你可以使用存放在 @yii/rbac/migrations 目錄中的數據庫遷移文件來做這件事(譯者注:根據本人經驗,最好是將授權數據初始化命令也寫到這個 RBAC 數據庫遷移文件中):
yii migrate --migrationPath=@yii/rbac/migrations
現在可以通過 Yii::$app->authManager 訪問 authManager 。
建立授權數據
所有授權數據相關的任務如下:
定義角色和權限;
建立角色和權限的關系;
定義規則;
將規則與角色和權限作關聯;
指派角色給用戶。
根據授權的彈性需求,上述任務可用不同的方法完成。
如果你的權限層次結構不會發生改變,而且你的用戶數是恒定的,你可以通過 authManager 提供的 API 創建一個 控制臺命令 一次性初始化授權數據:
authManager; // 添加 "createPost" 權限 $createPost = $auth->createPermission("createPost"); $createPost->description = "Create a post"; $auth->add($createPost); // 添加 "updatePost" 權限 $updatePost = $auth->createPermission("updatePost"); $updatePost->description = "Update post"; $auth->add($updatePost); // 添加 "author" 角色并賦予 "createPost" 權限 $author = $auth->createRole("author"); $auth->add($author); $auth->addChild($author, $createPost); // 添加 "admin" 角色并賦予 "updatePost" // 和 "author" 權限 $admin = $auth->createRole("admin"); $auth->add($admin); $auth->addChild($admin, $updatePost); $auth->addChild($admin, $author); // 為用戶指派角色。其中 1 和 2 是由 IdentityInterface::getId() 返回的id (譯者注:user表的id) // 通常在你的 User 模型中實現這個函數。 $auth->assign($author, 2); $auth->assign($admin, 1); } }
在用 yii rbac/init 執行了這個命令后,我們將得到下圖所示的層次結構:
作者可創建新貼,管理員可編輯帖子以及所有作者可做的事情。
如果你的應用允許用戶注冊,你需要在注冊時給新用戶指派一次角色。
例如, 在高級項目模板中,要讓所有注冊用戶成為作者,你需要如下例所示修改 frontendmodelsSignupForm::signup() 方法:
public function signup(){ if ($this->validate()) { $user = new User(); $user->username = $this->username; $user->email = $this->email; $user->setPassword($this->password); $user->generateAuthKey(); $user->save(false); // 要添加以下三行代碼: $auth = Yii::$app->authManager; $authorRole = $auth->getRole("author"); $auth->assign($authorRole, $user->getId()); return $user; } return null; }
對于有動態更改授權數據的復雜存取控制需求的,你可能需要使用 authManager 提供的 API 的開發用戶界面(例如:管理面板)。
使用規則 (Rules)如前所述,規則給角色和權限增加額外的約束條件。規則是 yiirbacRule 的派生類。 它需要實現 yiirbacRule::execute() 方法。
在之前我們創建的層次結構中,作者不能編輯自己的帖子,我們來修正這個問題。 首先我們需要一個規則來認證當前用戶是帖子的作者:
namespace app bac; use yii bacRule; /** * 檢查 authorID 是否和通過參數傳進來的 user 參數相符 */class AuthorRule extends Rule{ public $name = "isAuthor"; /** * @param string|integer $user 用戶 ID. * @param Item $item 該規則相關的角色或者權限 * @param array $params 傳給 ManagerInterface::checkAccess() 的參數 * @return boolean 代表該規則相關的角色或者權限是否被允許 */ public function execute($user, $item, $params) { return isset($params["post"]) ? $params["post"]->createdBy == $user : false; } }
上述規則檢查 post 是否是 $user 創建的。我們還要在之前的命令中 創建一個特別的權限 updateOwnPost :
$auth = Yii::$app->authManager; // 添加規則 $rule = new app bacAuthorRule;$auth->add($rule); // 添加 "updateOwnPost" 權限并與規則關聯 $updateOwnPost = $auth->createPermission("updateOwnPost"); $updateOwnPost->description = "Update own post"; $updateOwnPost->ruleName = $rule->name; $auth->add($updateOwnPost); // "updateOwnPost" 權限將由 "updatePost" 權限使用 $auth->addChild($updateOwnPost, $updatePost); // 允許 "author" 更新自己的帖子 $auth->addChild($author, $updateOwnPost);使用默認角色
所謂默認角色就是 隱式 地指派給 所有 用戶的角色。不需要調用 yiirbacManagerInterface::assign() 方法做顯示指派,并且授權數據中不包含指派信息。
默認角色通常與一個規則關聯,用以檢查該角色是否符合被檢查的用戶。
默認角色常常用于已經確立了一些角色的指派關系的應用(譯者注:指派關系指的是應用業務邏輯層面, 并非指授權數據的結構)。比如,一個應用的 user 表中有一個 group 字段,代表用戶屬于哪個特權組。 如果每個特權組可以映射到 RBAC 的角色,你就可以采用默認角色自動地為每個用戶指派一個 RBAC 角色。 讓我們用一個例子展示如何做到這一點。
假設在 user 表中,你有一個 group 字段,用 1 代表管理員組,用 2 表示作者組。 你規劃兩個 RBAC 角色 admin 和 author 分別對應這兩個組的權限。 你可以這樣設置 RBAC 數據,
namespace app bac; use Yii; use yii bacRule; /** * 檢查是否匹配用戶的組 */class UserGroupRule extends Rule{ public $name = "userGroup"; public function execute($user, $item, $params) { if (!Yii::$app->user->isGuest) { $group = Yii::$app->user->identity->group; if ($item->name === "admin") { return $group == 1; } elseif ($item->name === "author") { return $group == 1 || $group == 2; } } return false; } } $auth = Yii::$app->authManager; $rule = new app bacUserGroupRule; $auth->add($rule); $author = $auth->createRole("author"); $author->ruleName = $rule->name; $auth->add($author); // ... 添加$author角色的子項部分代碼 ... (譯者注:省略部分參照之前的控制臺命令) $admin = $auth->createRole("admin"); $admin->ruleName = $rule->name; $auth->add($admin); $auth->addChild($admin, $author); // ... 添加$admin角色的子項部分代碼 ... (譯者注:省略部分參照之前的控制臺命令)
注意,在上述代碼中,因為 "author" 作為 "admin" 的子角色,當你實現這個規則的 execute() 方法時, 你也需要遵從這個層次結構。
這就是為何當角色名為 "author" 的情況下(譯者注:$item->name就是角色名), execute() 方法在組為 1 或者 2 時均要返回 true (意思是用戶屬于 "admin" 或者 "author" 組 )。
接下來,在配置 authManager 時指定 yiirbacBaseManager::$defaultRoles 選項(譯者注:在應用配置文件中的組件部分配置):
return [ // ... "components" => [ "authManager" => [ "class" => "yii bacPhpManager", "defaultRoles" => ["admin", "author"], ], // ... ], ];
現在如果你執行一個存取權限檢查, 判定該規則時, admin 和 author 兩個角色都將會檢查。如果規則返回 true ,意思是角色符合當前用戶。基于上述規則 的實現,意味著如果某用戶的 group 值為 1 , admin 角色將賦予該用戶, 如果 group 值是 2 則將賦予author 角色。處理密碼(Passwords)
好的安全策略對任何應用的健康和成功極其重要。
不幸的是,許多開發者在遇到安全問題時,因為認識不夠或者實現起來比較麻煩,都不是很注意細節。
為了讓你的 Yii 應用程序盡可能的安全, Yii 囊括了一些卓越并簡單易用的安全特性。
密碼的哈希與驗證大部分開發者知道密碼不能以明文形式存儲,但是許多開發者仍認為使用 md5 或者 sha1 來哈希化密碼是安全的。
一度,使用上述的哈希算法是足夠安全的,但是,現代硬件的發展使得短時間內暴力破解上述算法生成的哈希串成為可能。
為了即使在最糟糕的情況下(你的應用程序被破解了)也能給用戶密碼提供增強的安全性,你需要使用一個能夠對抗暴力破解攻擊的哈希算法,目前最好的選擇是 bcrypt。
在 PHP 中,你可以通過 crypt 函數 生成 bcrypt 哈希。Yii 提供了兩個幫助函數以讓使用crypt 來進行安全的哈希密碼生成和驗證更加容易。
當一個用戶為第一次使用,提供了一個密碼時(比如:注冊時),密碼就需要被哈希化。
$hash = Yii::$app->getSecurity()->generatePasswordHash($password);
哈希串可以被關聯到對應的模型屬性,這樣,它可以被存儲到數據庫中以備將來使用。
當一個用戶嘗試登錄時,表單提交的密碼需要使用之前的存儲的哈希串來驗證:
if (Yii::$app->getSecurity()->validatePassword($password, $hash)) { // all good, logging user in } else { // wrong password }生成偽隨機數
偽隨機數據在許多場景下都非常有用。
比如當通過郵件重置密碼時,你需要生成一個令牌,將其保存到數據庫中,并通過郵件發送到終端用戶那里以讓其證明其對某個賬號的所有權。
這個令牌的唯一性和難猜解性非常重要,否則,就存在攻擊者猜解令牌,并重置用戶的密碼的可能性。
Yii 安全助手使得生成偽隨機數據非常簡單:
$key = Yii::$app->getSecurity()->generateRandomString();
注意,你需要安裝有 openssl 擴展,以生成密碼的安全隨機數據。
加密與解密Yii 提供了方便的幫助函數來讓你用一個安全秘鑰來加密解密數據。數據通過加密函數進行傳輸,這樣只有擁有安全秘鑰的人才能解密。
比如,我們需要存儲一些信息到我們的數據庫中,但是,我們需要保證只有擁有安全秘鑰的人才能看到它(即使應用的數據庫泄露)
// $data and $secretKey are obtained from the form $encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); // store $encryptedData to database
隨后,當用戶需要讀取數據時:
// $secretKey is obtained from user input, $encryptedData is from the database $data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);校驗數據完整性
有時,你需要驗證你的數據沒有第三方篡改或者使用某種方式破壞了。Yii 通過兩個幫助函數,提供了一個簡單的方式來進行數據的完整性校驗。
首先,將由安全秘鑰和數據生成的哈希串前綴到數據上。
// $secretKey our application or user secret, $genuineData obtained from a reliable source $data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);
驗證數據完整性是否被破壞了。
// $secretKey our application or user secret, $data obtained from an unreliable source $data = Yii::$app->getSecurity()->validateData($data, $secretKey);
你同樣可以給控制器或者 action 設置它的 enableCsrfValidation 屬性來多帶帶禁用 CSRF 驗證。
namespace appcontrollers; use yiiwebController; class SiteController extends Controller{ public $enableCsrfValidation = false; public function actionIndex() { // CSRF validation will not be applied to this and other actions } }
為了給某個定制的 action 關閉 CSRF 驗證,你可以:
namespace appcontrollers; use yiiwebController; 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); } }一些最佳安全實踐(Best Practices)
基本準則無論是開發何種應用程序,我們都有兩條基本的安全準則:
過濾輸入
轉義輸出
過濾輸入過濾輸入的意思是,用戶輸入不應該認為是安全的,你需要總是驗證你獲得的輸入值是在允許范圍內。
比如,我們假設 sorting 只能指定為 title, created_at 和 status 三個值,然后,這個值是由用戶輸入提供的,那么,最好在我們接收參數的時候,檢查一下這個值是否是指定的范圍。
對于基本的 PHP 而言,上述做法類似如下:
$sortBy = $_GET["sort"]; if (!in_array($sortBy, ["title", "created_at", "status"])) { throw new Exception("Invalid sort value."); }
在 Yii 中,很大可能性,你會使用 表單校驗器 來執行類似的檢查。
轉義輸出轉義輸出的意思是,根據我們使用數據的上下文環境,數據需要被轉義。
比如:在 HTML 上下文,你需要轉義 <,> 之類的特殊字符。在 JavaScript 或者 SQL 中,也有其他的特殊含義的字符串需要被轉義。
由于手動的給所用的輸出轉義容易出錯,Yii 提供了大量的工具來在不同的上下文執行轉義。
避免 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();防止 XSS 攻擊
XSS 或者跨站腳本發生在輸出 HTML 到瀏覽器時,輸出內容沒有正確的轉義。
例如,如果用戶可以輸入其名稱,那么他輸入 而非其名字 Alexander,所有輸出沒有轉義直接輸出用戶名的頁面都會執行 JavaScript 代碼 alert("Hello!");,這會導致瀏覽器頁面上出現一個警告彈出框。
就具體的站點而言,除了這種無意義的警告輸出外,這樣的腳本可以以你的名義發送一些消息到后臺,甚至執行一些銀行交易行為。
避免 XSS 攻擊在 Yii 中非常簡單,有如下兩種一般情況:
你希望數據以純文本輸出。
你希望數據以 HTML 形式輸出。
如果你需要的是純文本,你可以如下簡單的轉義:
= yiihelpersHtml::encode($username) ?>
如果是 HTML ,我們可以用 HtmlPurifier 幫助類來執行:
= yiihelpersHtmlPurifier::process($description) ?>
注意: HtmlPurifier 幫助類的處理過程較為費時,建議增加緩存:
防止 CSRF 攻擊
CSRF 是跨站請求偽造的縮寫。這個攻擊思想源自許多應用程序假設來自用戶的瀏覽器請求是由用戶自己產生的,而事實并非如此。
比如說:an.example.com 站點有一個 /logout URL,當以 GET 請求訪問時,登出用戶。如果它是由用戶自己操作的,那么一切都沒有問題。但是,有一天壞人在一個用戶經常訪問的論壇發了一個
內容的帖子。瀏覽器無法辨別請求一個圖片還是一個頁面,所以,當用戶打開含有上述標簽的頁面時,他將會從 an.example.com 登出。
上面就是最原始的思想。有人可能會說,登出用戶也不是什么嚴重問題,然而,我們發送一些 POST 數據其實也不是很麻煩的事情。
為了避免 CSRF 攻擊,你總是需要:
遵循 HTTP 準則,比如 GET 不應該改變應用的狀態。
保證 Yii CSRF 保護開啟。
防止文件暴露默認的服務器 webroot 目錄指向包含有 index.php 的 web 目錄。
在共享托管環境下,這樣是不可能的,這樣導致了所有的代碼,配置,日志都在webroot目錄。
如果是這樣,別忘了拒絕除了 web 目錄以外的目錄的訪問權限。如果沒法這樣做,考慮將你的應用程序托管在其他地方。
在生產環境關閉調試信息和工具在調試模式下, Yii 展示了大量的錯誤信息,這樣是對開發有用的。同樣,這些調試信息對于攻擊者而言也是方便其用于破解數據結構,配置值,以及你的部分代碼。
永遠不要在生產模式下將你的 index.php 中的 YII_DEBUG 設置為 true。
你同樣也不應該在生產模式下開啟 Gii。
它可以被用于獲取數據結構信息,代碼,以及簡單的用 Gii 生成的代碼覆蓋你的代碼。
調試工具欄同樣也應該避免在生產環境出現,除非非常有必要。它將會暴露所有的應用和配置的詳情信息。如果你確定需要,反復確認其訪問權限限定在你自己的 IP。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/21642.html
摘要:簡述是一個強大的代碼生成器,主要用于后臺代碼生成。下面列出由生成的文件,以便你研習功能和實現,或修改它們控制器模型和視圖補充被設計成高度可定制和可擴展的代碼生成工具。使用生成代碼是一個基于界面的代碼生成工具。 簡述 Gii 是一個強大的代碼生成器,主要用于后臺代碼生成。 開始 Gii Gii 是 Yii 中的一個模塊。可以通過配置應用的 yiibaseApplication::modu...
摘要:它由一個或多個類組成,它們在控制臺環境下通常被稱為命令。控制臺入口腳本通常被稱為,位于應用程序的根目錄。選項通過覆蓋在中的方法,你可以指定可用于控制臺命令選項。參數將傳遞給請求的子命令對應的操作方法。通常,執行成功的命令會返回。 簡述 控制臺應用程序的結構非常類似于 Yii 的一個 Web 應用程序,主要用于終端服務器執行。 控制臺命令 控制臺應用程序的結構非常類似于 Yii 的一個 ...
摘要:把所有的增量數據庫遷移提交到生產環境數據庫當中。如果其中任意一個遷移提交失敗了,那么這條命令將會退出并停止剩下的那些還未執行的遷移。執行這條命令期間不會有任何的遷移會被提交或還原。 簡述 數據遷移就是數據庫表在團隊建的遷移操作,達到團隊相互間的信息同步,數據統一。 數據庫遷移 一般步驟: 1、在 yii2 的 migrate 中,通常用來對數據庫數據表進行修改操作,主要對結構和小部分數...
摘要:簡述這里簡單歸納總結關于的錯誤處理和日志記錄的操作。錯誤處理器會正確地設置響應的狀態碼并使用合適的錯誤視圖頁面來顯示錯誤信息。記錄一個警告消息用來指示一些已經發生的意外。的義務是正確處理日志消息。相應的消息通過被記錄。 簡述 這里簡單歸納總結關于Yii的錯誤處理和日志記錄的操作。 錯誤處理(Errors) Yii 內置了一個yiiwebErrorHandler錯誤處理器,它使錯誤處理更...
摘要:簡述模塊是中的架構的板塊,主要負責數據的展示,渲染模板文件,展示數據內容。此外在一個視圖中還可以引入多個視圖文件,也是通過方法實現。布局文件的數據默認以顯示,也可以用數據塊的形式渲染到視圖上。必須要確認生成一次,才會正式生成新首頁。 簡述 View模塊是Yii中的MVC架構的V板塊,主要負責數據的展示,渲染模板文件,展示數據內容。 基本概念 MVC在Yii里面有一個Views文件夾,里...
閱讀 1364·2021-11-15 11:45
閱讀 3130·2021-09-27 13:36
閱讀 2876·2019-08-30 15:54
閱讀 993·2019-08-29 12:38
閱讀 2912·2019-08-29 11:22
閱讀 2995·2019-08-26 13:52
閱讀 2040·2019-08-26 13:30
閱讀 592·2019-08-26 10:37