首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在php中加密/解密数据?

如何在php中加密/解密数据?
EN

Stack Overflow用户
提问于 2012-06-06 14:28:52
回答 6查看 224K关注 0票数 118

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

我想做的是:

我有一个由(UserID、Fname、Lname、Email、Password)字段组成的表

我想要的是对所有字段进行加密,然后解密(如果没有加密算法,是否可以使用sha256进行加密/解密)

我想学习的另一件事是如何创建一个单向的hash(sha256)和一个好的“盐”。(基本上,我只是想要一个简单的加密/解密实现,hash(sha256)+salt)先生/女士,您的回答会有很大帮助,非常感谢。谢谢you++

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-06-08 08:00:35

前言

从表定义开始:

代码语言:javascript
复制
- UserID
- Fname
- Lname
- Email
- Password
- IV

以下是一些变化:

  1. 字段FnameLnameEmail将使用OpenSSL提供的对称密码加密,
  2. IV字段将存储用于加密的初始化向量。存储需求取决于所使用的密码和模式;稍后将详细介绍。
  3. Password字段将使用单向密码哈希进行散列,

加密

密码与模式

选择最佳的加密密码和模式超出了这个答案的范围,但最终的选择会影响加密密钥和初始化向量的大小;对于本文,我们将使用AES-256-CBC,它的固定块大小为16字节,密钥大小为16、24或32字节。

加密密钥

一个好的加密密钥是由一个可靠的随机数生成器生成的二进制blob。建议使用以下示例(>= 5.3):

代码语言:javascript
复制
$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:

代码语言:javascript
复制
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);

示例

让我们使用前面的$encryption_key$iv加密name字段;要做到这一点,我们必须将数据填充到块大小:

代码语言:javascript
复制
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%的存储空间。

解密

存储值的解密类似于:

代码语言:javascript
复制
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
));

认证加密

您可以通过附加从秘密密钥(不同于加密密钥)和密码文本生成的签名来进一步提高生成的密码文本的完整性。在解密密码文本之前,首先验证签名(最好采用常数时间比较方法)。

示例

代码语言:javascript
复制
// 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 (又名Blowfish)

这个答案将使用bcrypt的例子。

世代

可以生成这样的密码哈希:

代码语言:javascript
复制
$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);

salt与openssl_random_pseudo_bytes()一起生成,形成一个随机的数据块,然后通过base64_encode()strtr()运行,以匹配所需的[A-Za-z0-9/.]字母表。

crypt()函数基于算法($2y$ for Blowfish)、成本因子(在3 3GHz机器上约为0.40)和22个字符的盐执行散列。

验证

一旦获取了包含用户信息的行,就可以以以下方式验证密码:

代码语言:javascript
复制
$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值传递。如果给定密码与哈希匹配,则返回值将产生相同的哈希。为了验证哈希,通常建议使用常数时间比较函数来避免定时攻击。

用PHP5.5对密码进行散列

PHP5.5介绍了可以用来简化上述散列方法的密码散列函数

代码语言:javascript
复制
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);

并核实:

代码语言:javascript
复制
if (password_verify($given_password, $db_hash)) {
    // password valid
}

另见:password_hash()password_verify()

票数 297
EN

Stack Overflow用户

发布于 2012-06-06 14:37:22

我认为before...but已经回答了这个问题,如果您想加密/解密数据,就不能使用SHA256

代码语言:javascript
复制
//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);
票数 24
EN

Stack Overflow用户

发布于 2012-06-07 05:16:42

回答背景和解释

要理解这个问题,您必须首先了解SHA256是什么。SHA256是一个密码哈希函数。密码哈希函数是一个单向函数,其输出是加密安全的.这意味着计算哈希(相当于加密数据)很容易,但使用哈希(相当于解密数据)很难获得原始输入。由于使用加密哈希函数意味着解密在计算上是不可行的,因此您不能使用SHA256执行解密。

您想要使用的是一个双向函数,但更具体地说,是一个块密码。允许对数据进行加密和解密的函数。默认情况下,函数mcrypt_encryptmcrypt_decrypt使用Blowfish算法。在这个手册中可以找到PHP对mcrypt的使用。选择密码mcrypt使用的密码定义列表也存在。在维基百科可以找到一种关于河豚的wiki。块密码用已知密钥以已知大小和位置的块加密输入,以便以后可以使用密钥解密数据。这是SHA256不能提供给你的。

代码

代码语言:javascript
复制
$key = 'ThisIsTheCipherKey';

$ciphertext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, 'This is plaintext.', MCRYPT_MODE_CFB);

$plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10916284

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档