php加密


1.1.2.1
加密
PHP 5.5
以后,提供了新的密码加密函数password_hash(),这个加密函数有三个参数,第一个参数就是待加密的密码,第二个参数是加密算法,推荐使用PASSWORD_DEFAULT,这样可以随着PHP的升级,自动选用新的升级算法,第三个参数是加密算法执行次数,一般来说次数越多,密码强度越大,但是花费的时间越多。
1.1.2.2
校验
使用password_hash()加密时,每调用一次就会使用新的solt,所以即使是同样的密码,每次调用password_hash()的结果都是不一样的。
一般传统的MD5类方式加密密码时,判断输入的密码是否正确,就是重新用加密算法把密码再加密一次,然后判断加密的结果与数据库中保存的是否一致。现在使用这样的方式来校验密码自然是不行的了(注:ecshopectouch就是用这种方法存密码的)。
PHP
提供了password_verify()函数用来校验密码是否正确,这个函数有两个参数,第一个是用户输入的密码,第二个参数是事先保存的密码hash值。既然每次调用password_hash()的返回值都不一样,那password_verify()又是怎么确认密码是正确的呢?
 
根据PHP官网对于该函数的说明:
注意 password_hash() 返回的哈希包含了算法、 cost 和盐值。 因此,所有需要的信息都包含内。使得验证函数不需要储存额外盐值等信息即可验证哈希。
1.1.3  Yii 2
的封装
Yii 2
提供了一个Security类,将上面的密码加密和验证函数封装起来,并且增加了对于低版本PHP的支持。
generatePasswordHash

生成密码的哈希值,调用password_hash()实现。
validatePassword

校验密码是否正确,调用password_verify()实现。
 
在框架中使用Security类,无需自己初始化,Yii 2框架已经默认创建了Security类的实例,使用如下代码访问即可:
Yii::$app->security->validatePassword($password, $this->password_hash);
//或者
Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey)
 
Security
类的初始化?在base\Application.php中的preInit()函数中调用coreComponents()函数获得所配置的security属性对应的类路径:
public function coreComponents()
 {
     return [
         "log" => ["class" => "yii\log\Dispatcher"],
         "view" => ["class" => "yii\web\View"],
         "formatter" => ["class" => "yii\i18n\Formatter"],
         "i18n" => ["class" => "yii\i18n\I18N"],
         "mailer" => ["class" => "yii\swiftmailer\Mailer"],
         "urlManager" => ["class" => "yii\web\UrlManager"],
         "assetManager" => ["class" => "yii\web\AssetManager"],
         "security" => ["class" => "yii\base\Security"],
     ];
 }
1.1.4  题外话 —— 时序攻击
Security类的代码发现,其有一个compareString()函数,从功能上分析,它就是比较两个字符串是否相等,但是为什么不直接使用“==”来比较呢?看这个函数的说明:“Performs string comparison using timing attack resistant approach”
 
这里的“timing attack”(时序攻击)引起了我的兴趣,于是了解了一下,原来是有些破解算法是根据目标系统的运行时间,来推测出加密算法的一些信息,从而降低破解难度,提高破解速度,真的是很有意思的一种破解思路。
 
参考文献:
如何通俗地解释时序攻击(timingattack)?



1.Yii2.0加密及校验:
加密:$hash_password = Yii::$app->security->generatePasswordHash('123456');

校验:$bool = Yii::$app->security->validatePassword($password, $hash_password);
$password为输入的密码,它是没有经过加密的字符串
$hash_password为原先加密的密码

一般用法:
step1:将密码'123456'加密为$hash_password;
step2:将$hash_password存入数据库;
step3:从数据库中查询出密码,赋给变量$hash_password;
step4:获取用户输入的密码,赋给变量$password;
step5:校验密码,返回bool值.



2.PHP原生加密函数及校验函数:

加密函数(两个函数):
a函数:string password_hash ( string $password , integer $algo [, array $options ] );

b函数:string crypt(str,salt)
PHP校验函数:
a函数校验: boolean password_verify ( string $password , string $hash );

b函数校验: 使用crypt()加入对应的盐值(salt)加密后比较两个字符串.



Yii密码加密与验证(源码分析)
发表于2016/7/27 9:14:10  1469人阅读
分类: Yii2.0

        yii
权威指南上,关于处理密码,提供了两种帮助函数,那么其工作原理是什么呢?

public function generatePasswordHash($password, $cost = null)
{
    if ($cost === null) {
        $cost = $this->passwordHashCost;
    }

    if (function_exists('password_hash')) {//
如果存在password_hash()方法,就使用该方法返回加密后的结果
        /** @noinspection PhpUndefinedConstantInspection */
        return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
    }

    $salt = $this->generateSalt($cost);//
如果不存在password_hash()方法,就使用cypt+salt的形式返回哈希化的结果
    $hash = crypt($password, $salt);
    // strlen() is safe since crypt() returns only ascii
    if (!is_string($hash) || strlen($hash) !== 60) {
        throw new Exception('Unknown error occurred while generating hash.');
    }

    return $hash;
}


        关于上述代码,需要讲解的是
        password_hash()
,在(PHP 5 >= 5.5.0, PHP 7)中,提供的新函数,用于创建哈希密码:
        
string password_hash ( string $password , integer $algo [, array $options ] )
       
第一个参数为所要加密的用户密码,第二个参数为密码算法常数(PASSWORD_DEFAULT, PASSWORD_BCRYPT)第三个参数为选项
       
具体用法详见http://php.net/manual/en/function.password-hash.php 这里不在做过多解释
        
        
哈希值创建完毕,我们可以用password_verify()来校验密码是否和哈希值匹配:
        boolean password_verify ( string $password , string $hash )
,它接收2个参数:密码和哈希值,并返回布尔值,可以用于检查之前生成的哈希值是否和密码匹配。

       
接下来咱们重点分析一下使用crypt()函数加盐值的方式:
protected function generateSalt($cost = 13)
{
    $cost = (int) $cost;
    if ($cost < 4 || $cost > 31) {
        throw new InvalidParamException('Cost must be between 4 and 31.');
    }

    // Get a 20-byte random string
    $rand = $this->generateRandomKey(20);
    // Form the prefix that specifies Blowfish (bcrypt) algorithm and cost parameter.
    $salt = sprintf("$2y$%02d$", $cost);
    // Append the random salt data in the required base64 format.
    $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));

    return $salt;
}

    salt的生成:
    sprintf()函数:把百分号(%)符号替换成一个作为参数进行传递的变量
    $salt = sprintf("$2y$%02d$",$cost); = $2y$13$
    generateRandomKey()方法为yii生成了一个20字节长度的随机数
    然后最后将$rand使用base64_encode加密取前0-22个,将+换为.,然后前面在拼接上$salt
    yii使用的是如下方法



        就此加密过程就结束了
       
关于验证密码:
public function validatePassword($password, $hash)
{
    if (!is_string($password) || $password === '') {
        throw new InvalidParamException('Password must be a string and cannot be empty.');
    }

    if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches)
        || $matches[1] < 4
        || $matches[1] > 30
    ) {
        throw new InvalidParamException('Hash is invalid.');
    }

    if (function_exists('password_verify')) {//
如果存在passwo_verify则直接使用其验证密码,和password_hash()配套
        return password_verify($password, $hash);
    }

    $test = crypt($password, $hash); //
将用户输入的密码和哈希密码进行crypt()加密
    $n = strlen($test);//
获取字节长度
    if ($n !== 60) {
        return false;
    }

    return $this->compareString($test, $hash);//compareString
慢比较,两个密码,并返回Boolean类型
}

public function compareString($expected, $actual)
{
    $expected .= "\0";
    $actual .= "\0";
    $expectedLength = StringHelper::byteLength($expected);//mb_strlen($var,8bit)
计算长度的,以防编码不一造成长度计算不一致
    $actualLength = StringHelper::byteLength($actual);
    $diff = $expectedLength - $actualLength;//
两个长度差
    for ($i = 0; $i < $actualLength; $i++) {//
逐一比较
        // $diff|= ()
等价于 $diff = $diff | ()  取或  此处,一旦为结果为1,则不可能置零
        // ^
为按位异或,相同为0,不同非0
        $diff |= (ord($actual[$i]) ^ ord($expected[$i % $expectedLength]));
    }
    //
如果按位取或之后,$diff仍然为0,则说明两密码绝对相等
    return $diff === 0;
}

    为什么要用这种比较方法:
    让比较过程耗费固定的时间(全部比较完再返回结果)可以保证攻击者无法对一个在线系统使用计时攻击,以此获取
密码的哈希值,然后进行本地破解工作。 
    比较两个字节序列(字符串)的标准做法是,从第一字节开始,每个字节逐一顺序比较。只要发现某字节不相同了,
就可以立即返回的结果。如果遍历整个字符串也没有找到不同的字节,那么两个字符串就是相同的,并且返回
这意味着比较字符串的耗时决定于两个字符串到底有多大的不同。
   
举个例子,使用标准的方法比较“xyzabc”“abcxyz”,由于第一个字符就不同,不需要检查后面的内容就可以马上
返回结果。相反,如果比较“aaaaaaaaaaB”“aaaaaaaaaaZ”,比较算法就需要遍历最后一位前所有的“a”,然后才能知
道它们是不相同的。
   
攻击方式:首先攻击者准备256个字符串,它们的哈希值的第一字节包含了所有可能的情况。然后用它们去系统中尝
试登录,并记录系统返回结果所消耗的时间,耗时最长的那个就是第一字节猜对的那个。接下来用同样的方式猜测第二
字节、第三字节等等。直到攻击者获取了最够长的哈希值片段,最后只需在自己的机器上破解即可,完全不受在线系统的
限制。
     上述方法可以简化为如下方式:



private static boolean slowEquals(byte[] a, byte[] b)
{
    int diff = a.length ^ b.length;
    for(int i = 0; i < a.length && i < b.length; i++)
    diff |= a[i] ^ b[i];
    return diff === 0;

}




AES模式:

分组密码有五种工作体制:1. 电码本模式(Electronic Codebook Book (ECB));2.密码分组链接模式(Cipher Block Chaining (CBC));3.计算器模式(Counter (CTR));4.密码反馈模式(Cipher FeedBack (CFB));5.输出反馈模式(Output FeedBack (OFB))。

以下逐一介绍一下:

1.电码本模式( Electronic Codebook Book (ECB)

    这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。


2.密码分组链接模式(Cipher Block Chaining (CBC))

这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

 

3.计算器模式(Counter (CTR))

计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是 在计算器不能维持很长的情况下,密钥只能使用一次 。CTR的示意图如下所示:

 

4.密码反馈模式(Cipher FeedBack (CFB))

这种模式较复杂。

 

5.输出反馈模式(Output FeedBack (OFB))

    这种模式较复杂。



以下附上 C++ 源代码:
/**
*@autho stardust
*@time 2013-10-10
*@param
实现 AES 五种加密模式的测试
*/

# include <iostream>
using namespace std ;

// 加密编码过程函数 ,16 1 0
int dataLen = 16 ;   // 需要加密数据的长度
int encLen = 4 ;     // 加密分段的长度
int encTable[ 4 ] = { 1 , 0 , 1 , 0 };  // 置换表
int data[ 16 ] = { 1 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 }; // 明文
int ciphertext[ 16 ]; // 密文

// 切片加密函数
void encode ( int arr[])
{
   
for ( int i= 0 ;i<encLen;i++)
    {
        arr[i] = arr[i] ^ encTable[i];
    }
}

// 电码本模式加密, 4 位分段
void ECB ( int arr[])
{
   
// 数据明文切片
   
int a[ 4 ][ 4 ];
   
int dataCount = 0 // 位置变量
   
for ( int k= 0 ;k< 4 ;k++)
    {
       
for ( int t= 0 ;t< 4 ;t++)
        {
            a[k][t] = data[dataCount];
            dataCount++;
        }
    }
    dataCount =
0 ; // 重置位置变量
   
for ( int i= 0 ;i<dataLen;i=i+encLen)
    {
       
int r = i/encLen; //
       
int l = 0 ; //
       
int encQue[ 4 ]; // 编码片段
       
for ( int j= 0 ;j<encLen;j++)
        {
            encQue[j] = a[r][l];
            l++;
        }
        encode(encQue);
// 切片加密
       
// 添加到密文表中
       
for ( int p= 0 ;p<encLen;p++)
        {
            ciphertext[dataCount] = encQue[p];
            dataCount++;
        }
    }
   
cout << "ECB 加密的密文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<ciphertext[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}

//CBC
// 密码分组链接模式, 4 位分段
void CCB ( int arr[])
{
   
// 数据明文切片
   
int a[ 4 ][ 4 ];
   
int dataCount = 0 // 位置变量
   
for ( int k= 0 ;k< 4 ;k++)
    {
       
for ( int t= 0 ;t< 4 ;t++)
        {
            a[k][t] = data[dataCount];
            dataCount++;
        }
    }
    dataCount =
0 ; // 重置位置变量

   
int init[ 4 ] = { 1 , 1 , 0 , 0 };  // 初始异或运算输入
   
// 初始异或运算
   
for ( int i= 0 ;i<dataLen;i=i+encLen)
    {
       
int r = i/encLen; //
       
int l = 0 ; //
       
int encQue[ 4 ]; // 编码片段
       
// 初始化异或运算
       
for ( int k= 0 ;k<encLen;k++)
        {
            a[r][k] = a[r][k] ^ init[k];
        }
        
// Key 加密的单切片
       
for ( int j= 0 ;j<encLen;j++)
        {
            encQue[j] = a[r][j];
        }
        encode(encQue);
// 切片加密
       
// 添加到密文表中
       
for ( int p= 0 ;p<encLen;p++)
        {
            ciphertext[dataCount] = encQue[p];
            dataCount++;
        }
       
// 变换初始输入
       
for ( int t= 0 ;t<encLen;t++)
        {
            init[t] = encQue[t];
        }
    }


   
cout << "CCB 加密的密文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<ciphertext[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}

//CTR
// 计算器模式, 4 位分段
void CTR ( int arr[])
{
   
// 数据明文切片
   
int a[ 4 ][ 4 ];
   
int dataCount = 0 // 位置变量
   
for ( int k= 0 ;k< 4 ;k++)
    {
       
for ( int t= 0 ;t< 4 ;t++)
        {
            a[k][t] = data[dataCount];
            dataCount++;
        }
    }
    dataCount =
0 ; // 重置位置变量

   
int init[ 4 ][ 4 ] = {{ 1 , 0 , 0 , 0 },{ 0 , 0 , 0 , 1 },{ 0 , 0 , 1 , 0 },{ 0 , 1 , 0 , 0 }};  // 算子表
   
int l = 0 ; // 明文切片表列
   
// 初始异或运算
   
for ( int i= 0 ;i<dataLen;i=i+encLen)
    {
       
int r = i/encLen; //
       
int encQue[ 4 ]; // 编码片段
       
// 将算子切片
       
for ( int t= 0 ;t<encLen;t++)
        {
            encQue[t] = init[r][t];
        }
        encode(encQue);
// 算子与 key 加密
       
// 最后的异或运算
       
for ( int k= 0 ;k<encLen;k++)
        {
            encQue[k] = encQue[k] ^ a[l][k];
        }
        l++;

       
// 添加到密文表中
       
for ( int p= 0 ;p<encLen;p++)
        {
            ciphertext[dataCount] = encQue[p];
            dataCount++;
        }
    }


   
cout << "CTR 加密的密文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<ciphertext[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}

//CFB
// 密码反馈模式, 4 位分段
void CFB ( int arr[])
{
   
// 数据明文切片 , 切成 2 * 8
   
int a[ 8 ][ 2 ];
   
int dataCount = 0 // 位置变量
   
for ( int k= 0 ;k< 8 ;k++)
    {
       
for ( int t= 0 ;t< 2 ;t++)
        {
            a[k][t] = data[dataCount];
            dataCount++;
        }
    }
    dataCount =
0 // 恢复初始化设置
   
int lv[ 4 ] = { 1 , 0 , 1 , 1 };  // 初始设置的位移变量
   
int encQue[ 2 ]; //K 的高两位
   
int k[ 4 ]; //K

   
for ( int i= 0 ;i< 2 * encLen;i++) // 外层加密循环
    {
       
// 产生 K
       
for ( int vk= 0 ;vk<encLen;vk++)
        {
            k[vk] = lv[vk];
        }
        encode(k);
       
for ( int k2= 0 ;k2< 2 ;k2++)
        {
            encQue[k2] = k[k2];
        }
       
//K 与数据明文异或产生密文
       
for ( int j= 0 ;j< 2 ;j++)
        {
            ciphertext[dataCount] = a[dataCount/
2 ][j] ^ encQue[j];
            dataCount++;
        }
       
//lv 左移变换
        lv[
0 ] = lv[ 2 ];
        lv[
1 ] = lv[ 3 ];
        lv[
2 ] = ciphertext[dataCount -2 ];
        lv[
3 ] = ciphertext[dataCount -1 ];
    }

   
cout << "CFB 加密的密文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<ciphertext[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}

//OFB
// 输出反馈模式, 4 位分段
void OFB ( int arr[])
{
   
// 数据明文切片 , 切成 2 * 8
   
int a[ 8 ][ 2 ];
   
int dataCount = 0 // 位置变量
   
for ( int k= 0 ;k< 8 ;k++)
    {
       
for ( int t= 0 ;t< 2 ;t++)
        {
            a[k][t] = data[dataCount];
            dataCount++;
        }
    }
    dataCount =
0 // 恢复初始化设置
   
int lv[ 4 ] = { 1 , 0 , 1 , 1 };  // 初始设置的位移变量
   
int encQue[ 2 ]; //K 的高两位
   
int k[ 4 ]; //K

   
for ( int i= 0 ;i< 2 * encLen;i++) // 外层加密循环
    {
       
// 产生 K
       
for ( int vk= 0 ;vk<encLen;vk++)
        {
            k[vk] = lv[vk];
        }
        encode(k);
       
for ( int k2= 0 ;k2< 2 ;k2++)
        {
            encQue[k2] = k[k2];
        }
       
//K 与数据明文异或产生密文
       
for ( int j= 0 ;j< 2 ;j++)
        {
            ciphertext[dataCount] = a[dataCount/
2 ][j] ^ encQue[j];
            dataCount++;
        }
       
//lv 左移变换
        lv[
0 ] = lv[ 2 ];
        lv[
1 ] = lv[ 3 ];
        lv[
2 ] = encQue[ 0 ];
        lv[
3 ] = encQue[ 1 ];
    }

   
cout << "CFB 加密的密文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<ciphertext[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}


void printData ()
{
   
cout << " 以下示范 AES 五种加密模式的测试结果: " << endl ;
   
cout << "---------------------------------------------" << endl ;
   
cout << " 明文为: " << endl ;
   
for ( int t1= 0 ;t1<dataLen;t1++) // 输出密文
    {
       
if (t1!= 0 && t1% 4 == 0 )
           
cout << endl ;
       
cout <<data[t1]<< " " ;
    }
   
cout << endl ;
   
cout << "---------------------------------------------" << endl ;
}
int main ()
{
    printData();
    ECB(data);
    CCB(data);
    CTR(data);
    CFB(data);
    OFB(data);
   
return 0 ;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值