好问题
Good  Question
  • 首 页
  • 问题
    • PHP
    • JAVA
    • CPlusPlus
    • C#
    • SQL
  • 关 于
  • 联 系
如何用PHP来加密/解密文件? 关闭 返回上一级  

如何用PHP来加密/解密文件?
+ 查看更多

发布日期:2018-03-10 16:14
分类:PHP
浏览次数:214
我目前是一个正在学习PHP的学生,我正在尝试用PHP对数据做一个简单的加密/解密处理。我在网上做了一些研究,其中的一部分的确带给了我一些困扰(至少对于我来说)。
下面的就是我正在尝试做的事:
我有一个包含UserID,Fname,Lname,Email,Password这些字段的表
我想要做的就是把所有字段进行加密然后解密(如果没有任何加密算法,是否可以使用sha256用于加密/解密)。
另外一件我想学习的就是怎样结合一个好的“salt”创建一个单一的方法hash(sha256)(基本上我就是想实现一个简单的加密/解密方法,hash(sha256)+salt)
您的回答将对我产生莫大的帮助我将万分感激,谢谢!

回答:

前言
从您的表定义开始:
- UserID
- Fname
- Lname
- Email
- Password
- IV
下面是需要更改的地方:
 
  1. 通过提供OpenSSL, 字段Fname,Lname并且Email将使用对称加密;
  2. IV字段将存储用于加密的初始化向量,存储要求取决于使用的密码和模式,关于这一点后面我会详细介绍;
  3. 该Password字段将使用单向密码散列进行排列
 
加密
 
密码和模式
选择最好的加密密码和模式超出了这个问题答案的范围,但最终的选择影响加密密钥和初始化向量的大小,对于这个帖子,我们将使用AES-256-CBC,其具有16字节的固定块大小和16,24或32字节的密钥大小。
 
加密密钥
良好的加密密钥是由可靠的随机数生成器生成的二进制Blob。将推荐以下示例(> = 5.3):
$key_size = 32; // 256 bits
$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);
// $strong will be true if the key is crypto safe
这可以执行一次或多次(如果您希望创建一系列加密密钥),尽量保持这些为私有的。
 
IV
初始化向量增加了加密的随机性并且需要CBC模式的支持。这些值应该只能使用一次(从技术上看每个加密密钥一次)因此对行的任何部分的更新都应重新生成它。我这里提供了一个函数帮你生成IV
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
示例, 让我们用前面的$encryption_key和$iv给名字字段加密,要做到这一点,我们需要将数据填满数据块。
function pkcs7_pad($data, $size)
{
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}

$name = 'Jack';
$enc_name = openssl_encrypt(
    pkcs7_pad($name, 16), // padded data
    'AES-256-CBC',        // cipher and mode
    $encryption_key,      // secret key
    0,                    // options (not used)
    $iv                   // initialisation vector
);
存储要求
像IV这样的输出值是二进制的,要在MySQL中存储这些值,请考虑使用BINARY或VARBINARY列。如果这个不可行的话,您还可以使用base64_encode()或bin2hex()将二进制数据转换为文本表示,这样做的话会占用多余的33%-100%的存储空间。

解密

存储值的解密也是类似的:
function pkcs7_unpad($data)
{
    return substr($data, 0, -ord($data[strlen($data) - 1]));
}

$row = $result->fetch(PDO::FETCH_ASSOC); // read from database result
// $enc_name = base64_decode($row['Name']);
// $enc_name = hex2bin($row['Name']);
$enc_name = $row['Name'];
// $iv = base64_decode($row['IV']);
// $iv = hex2bin($row['IV']);
$iv = $row['IV'];

$name = pkcs7_unpad(openssl_decrypt(
    $enc_name,
    'AES-256-CBC',
    $encryption_key,
    0,
    $iv
));
验证加密
你可以通过从解密秘钥(不同于加密秘钥)和密文追加生成的签名来进一步提高生成的密文的完整性。在密文被解密之前,首先验证签名(优选地使用时间常数比较方法)。
例如:
// generate once, keep safe
$auth_key = openssl_random_pseudo_bytes(32, $strong);

// authentication
$auth = hash_hmac('sha256', $enc_name, $auth_key, true);
$auth_enc_name = $auth . $enc_name;

// verification
$auth = substr($auth_enc_name, 0, 32);
$enc_name = substr($auth_enc_name, 32);
$actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true);

if (hash_equals($auth, $actual_auth)) {
    // perform decryption
}
也可以看看: hash_equals()
 
散列
必须尽可能避免在数据库中存储可逆密码; 你只希望验证密码,而不是知道其内容。如果用户丢失密码,最好允许他们重置密码,而不是向他们发送密码(确保密码重置只能在有限的时间内完成)。应用散列函数是一种单向操作; 之后可以安全地用于验证而不暴露原始数据;因为密码相对长度较短而且多数人选择了较差的密码,所以对于密码一种强力的方法是用一个可行的方法来揭开它。相对于已有的哈希值,用MD5或SHA1的哈希算法来验证文件内容。他们经过了极大的优化,可以尽可能快地完成验证,同时仍然保持准确无误。给定它们相对有限的输出空间,很容易建立一个已知密码和它们各自的哈希输出表,即彩虹表。在对密码进行哈希处理之前添加一个盐将会使一张彩虹表无用,但最近的硬件进步使得强制查找成为可行的方法。这就是为什么你需要一个特别慢而且根本不可能优化的哈希算法。它还应该能够增加加速硬件的负载,而不影响验证现有密码散列以使其成为不会过时的技术。
 
目前有两种流行的选择:
 
1. PBKDF2(基于密码的密钥导出函数v2)
2. Bcrypt(aka Blowfish)
这个答案将使用一个bcrypt的例子。
密码哈希可以这样生成:
$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('$2y$%02d$%s',
    13, // 2^n cost factor
    substr(strtr(base64_encode($random), '+', '.'), 0, 22)
);

$hash = crypt($password, $salt);
盐是通过openssl_random_pseudo_bytes()生成的以形成随机数据块,然后遍历base64_encode()和strtr()匹配所需的字母表[A-Za-z0-9/.]。这个crypt()函数基于算法($2y$对于Blowfish)执行哈希算法。成本因素(在3GHz机器上大约为0.1s 的因子为13)和22个字符的盐。
 
验证
一旦您提取了包含用户信息的行,您将以这种方式验证密码:
$given_password = $_POST['password']; // the submitted password
$db_hash = $row['Password']; // field with the password hash

$given_hash = crypt($given_password, $db_hash);

if (isEqual($given_hash, $db_hash)) {
    // user password verified
}

// constant time string compare
function isEqual($str1, $str2)
{
    $n1 = strlen($str1);
    if (strlen($str2) != $n1) {
        return false;
    }
    for ($i = 0, $diff = 0; $i != $n1; ++$i) {
        $diff |= ord($str1[$i]) ^ ord($str2[$i]);
    }
    return !$diff;
}
要验证密码,您可以再次调用crypt(),但将先前计算的哈希值作为salt值传递。如果给定的密码与散列匹配,则产生返回值相同的散列。为了验证散列,通常建议使用常数时间比较函数来避免定时攻击。即使被暴库,由于使用crypt算法,只要因子不被泄漏(即使泄漏也没太大事情),就不会被猜测密码。
PHP5.5中的密码散列
PHP 5.5引入了密码哈希函数,您可以使用它来简化上述哈希的方法:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
验证:
if (password_verify($given_password, $db_hash)) {
    // password valid
}
同时参考:password_hash(),password_verify()
上一篇PHP如何判断访问浏览器的版本是Internet Explorer 6,7,8或者9?
如何将JavaScript变量传递给PHP?下一篇
下一篇如何将JavaScript变量传递给PHP?

最新文章

  • 函数`__construct`用来干嘛的
    发布日期:2018-03-26
  • 通过访客的IP得到他们的地区
    发布日期:2018-03-26
  • 合并两个PHP对象的最好的方法是什么?
    发布日期:2018-03-26
  • 该如何把一该如何把一个对象转化成数组?
    发布日期:2018-03-26
  • 什么是输出缓冲区?
    发布日期:2018-03-26
  • 在PHP中怎么把用逗号分隔的字符串分隔在一个数组里?
    发布日期:2018-03-26
  • 在PHP中使用foreach循环时查找数组的最后一个元素
    发布日期:2018-03-26
关于好问
收集整理一些有用的问题和回答,造福中国的程序旺和IT喵们!
友情链接
起飞页 
相关信息
版权声明
Copyright © 2016 - 2017  苏州卡达网络科技有限公司 好问 GOODQ.TOP 备案号:苏ICP备09008221号-5