我们做程序的时候, 加密解密是绕不开的话题, 使用 yii2 开发应用的时候, 都内置了哪些有关加密解密 (安全) 方便的支持那? 本文将为你揭晓.
相关环境
操作系统及 IDE macOS 10.13.1 & PhpStorm2018.1.2
软件版本 php7.1.8 Yii2.0.14
在 yii2 中, 管理加密解密的库叫做 Security, 它以 yii2 组件的形式存在, 因此你可以通过 Yii::$app->security 来获取并使用它.
Security 组件源代码位置如下
vendor/yiisoft/yii2/base/Security.php
Security 组件一共有 15 个与加密解密 (& 编码) 相关的公共方法, 我们先来列一个清单.
- encryptByPassword
- encryptByKey
- decryptByPassword
- decryptByKey
- hkdf
- pbkdf2
- hashData
- validateData
- generateRandomKey
- generateRandomString
- generatePasswordHash
- validatePassword
- compareString
- maskToken
- unmaskToken
我想有一些你一定没见过, 没关系, 我们一一去了解.
generateRandomString
之所以先说 generateRandomString 是因为它最常用, 起码我是这样.
public function generateRandomString($length = 32){...}
生成一个随机的字符串, 参数 $length 代表这个字符串的长度, 默认 32 位. 值得说明的是这个字符串的取值为范围是[A-Za-z0-9_-].
generatePasswordHash & validatePassword
generatePasswordHash & validatePassword 经常被用来加密用户密码以及对密码是否正确的验证, 自从 MD5 可能被碰撞后, 我们用 yii2 开发应用的时候, generatePasswordHash 函数对密码进行加密就成为首选了, 它调用了 crypt 函数.
一般用法如下
- // 使用 generatePasswordHash 为用户的密码加密,$hash 存储到库中
- $hash = Yii::$app->getSecurity()->generatePasswordHash($password);
- // 使用 validatePassword 对密码进行验证
- if(Yii::$app->getSecurity()->validatePassword($password, $hash)){
- // 密码正确
- }else{
- // 密码错误
- }
- generateRandomKey
和 generateRandomString 类似, 生成一个随机的串, 参数为长度, 默认为 32 位, 区别在于 generateRandomKey 生成的不是 ASCII.
简单的说 generateRandomString 约等于 base64_encode(generateRandomKey).
encryptByPassword & decryptByPassword
编码和解码函数, 使用一个秘钥对数据进行编码, 然后通过此秘钥在对编码后的数据进行解码.
例子
- $dat = Yii::$app->security->encryptByPassword("hello","3166886");
- echo Yii::$app->security->encryptByPassword($dat,"3166886");// hello
要注意, 通过上面得到的编码后的数据不是 ASCII, 可以通过 base64_encode 和 base64_decode 在外层包装下.
encryptByKey & decryptByKey
同样是一组编码和解码函数, 比通过密码的方式要快. 函数声明为
- public function encryptByKey($data, $inputKey, $info = null){}
- public function decryptByKey($data, $inputKey, $info = null){}
encryptByKey & decryptByKey 存在着第三个参数, 比如我们可以传递会员的 ID 等, 这样此信息将和 $inputKey 一起作为加密解密的钥匙.
hkdf
使用标准的 HKDF 算法从给定的输入键中导出一个键. 在 PHP7 + 使用的是 hash_hkdf 方法, 小于 PHP7 使用 hash_hmac 方法.
pbkdf2
使用标准的 PBKDF2 算法从给定的密码导出一个密钥. 该方法可以用来进行密码加密, 不过 yii2 有更好的密码加密方案 generatePasswordHash.
hashData 和 validateData
有的时候为了防止内容被篡改, 我们需要对数据进行一些标记, hashData 和 validateData 就是完成这个任务的组合.
hashData 用来对原始数据进行加数据前缀, 比如如下代码
- $result = Yii::$app->security->hashData("hello",'123456',false);
- // ac28d602c767424d0c809edebf73828bed5ce99ce1556f4df8e223faeec60eddhello
你看到了在 hello 的前面多了一组字符, 这组字符会随着原始数据的不同而变化. 这样我们就对数据进行了特殊的防止篡改标记, 接下来是 validateData 上场了.
注意: hashData 的第三个参数代表生成的哈希值是否为原始二进制格式. 如果为 false, 则会生成小写十六进制数字.
validateData 对已经加了数据前缀的数据进行检测, 如下代码
- $result = Yii::$app->security->validateData("ac28d602c767424d0c809edebf73828bed5ce99ce1556f4df8e223faeec60eddhello",'123456',false);
- // hello
如果返回了原始的字符串则表示验证通过, 否则会返回假.
validateData 函数的第三个参数应该与使用 hashData() 生成数据时的值相同. 它指示数据中的散列值是否是二进制格式. 如果为 false, 则表示散列值仅由小写十六进制数字组成. 将生成十六进制数字.
compareString
可防止时序攻击的字符串比较, 用法非常简单.
Yii::$app->security->compareString("abc",'abc');
结果为真则相等, 否则不相等.
那么什么是时序攻击那? 我来举一个简单的例子.
- if($code == Yii::$app->request->get('code')){
- }
上面的比较逻辑, 两个字符串是从第一位开始逐一进行比较的, 发现不同就立即返回 false, 那么通过计算返回的速度就知道了大概是哪一位开始不同的, 这样就实现了电影中经常出现的按位破解密码的场景.
而使用 compareString 比较两个字符串, 无论字符串是否相等, 函数的时间消耗是恒定的, 这样可以有效的防止时序攻击.
maskToken && unmaskToken
maskToken 用于掩盖真实 token 且不可以压缩, 同一个 token 最后生成了不同的随机令牌, 在 yii2 的 csrf 功能上就使用了 maskToken, 原理并不复杂, 我们看下源码.
- public function maskToken($token){
- $mask = $this->generateRandomKey(StringHelper::byteLength($token));
- return StringHelper::base64UrlEncode($mask . ($mask ^ $token));
- }
而 unmaskToken 目的也很明确, 用于得到被 maskToken 掩盖的 token.
接下来我们看一个例子代码
- $token = Yii::$app->security->maskToken("123456");
- echo Yii::$app->security->unmaskToken($token);// 结果为 123456
最后我们总结下
加密 / 解密: encryptByKey(),decryptByKey(), encryptByPassword() 和 decryptByPassword();
使用标准算法的密钥推导: pbkdf2() 和 hkdf();
防止数据篡改: hashData() 和 validateData();
密码验证: generatePasswordHash() 和 validatePassword()
更多 Yii 原创文章 https://nai8.me/lang-1.html
来源: https://segmentfault.com/a/1190000015241037