如何在php中加密/解密数据?前言加密解密经过身份验证的加密散列法

我目前是一名学生,我正在学习PHP,我正在尝试在PHP中对数据进行简单的加密/解密。我做了一些在线研究,其中一些非常令人困惑(至少对我来说)。

以下是我正在尝试执行的操作:

我有一个由这些字段组成的表(用户ID,Fname,Lname,电子邮件,密码)

我想要的是将所有字段加密然后解密(是否可以用于加密/解密,如果不是任何加密算法)sha256

我想学习的另一件事是,如何创造一种与良好“盐”相结合的单一方式。(基本上,我只想有一个简单的加密/解密实现,先生/女士,您的答案将有很大的帮助,非常感谢。谢谢++hash(sha256)hash(sha256)+salt)


答案 1

前言

从表定义开始:

- UserID
- Fname
- Lname
- Email
- Password
- IV

以下是更改:

  1. 字段 ,并将使用 OpenSSL 提供的对称密码进行加密,FnameLnameEmail
  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

这可以完成一次或多次(如果您希望创建加密密钥链)。尽可能保持这些内容的私密性。

初始化向量为加密增加了随机性,并且是 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,是二进制的;在数据库中存储这些值可以通过使用指定的列类型(如 或 )来完成。BINARYVARBINARY

输出值与IV一样,是二进制的;要将这些值存储在MySQL中,请考虑使用二进制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(又名河豚)

这个答案将使用一个带有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() 函数根据算法(对于 Blowfish)、成本因子(在 3GHz 机器上因子 13 大约需要 0.40 秒)和 22 个字符的盐来执行哈希。$2y$

验证

读取包含用户信息的行后,请按以下方式验证密码:

$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;
}

若要验证密码,请再次调用,但将以前计算的哈希作为 salt 值传递。如果给定的密码与哈希匹配,则返回值将生成相同的哈希。要验证哈希,通常建议使用常量时间比较函数来避免计时攻击。crypt()

使用 PHP 5.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()


答案 2

我想这之前已经回答过了...但无论如何,如果你想加密/解密数据,你不能使用SHA256

//Key
$key = 'SuperSecretKey';

//To Encrypt:
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_ECB);

//To Decrypt:
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_ECB);

推荐