如何加密/解密数据在PHP? [英] How to encrypt/decrypt data in php?

查看:164
本文介绍了如何加密/解密数据在PHP?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前是个学生,我正在学习PHP,我想在PHP中进行简单的数据加密/解密。我做了一些在线研究,其中一些是很混乱的(至少对我来说)。



这里是我想做的:



我有一个包含这些字段的表格(UserID,Fname,Lname,Email,Password)



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



我想学习的另一件事是如何创建一个单向哈希(sha256)结合一个好的盐。
(基本上我只是想有一个简单的加密/解密实现, hash(sha256)+ salt)

Sir / Ma 你,你的答案将是很大的帮助,非常感谢。谢谢++

解决方案

前言



/ p>

   -  UserID 
- Fname
- Lname
- 电子邮件
- 密码
- IV

以下是更改:


  1. 字段 Fname Lname 将使用由 OpenSSL

  2. 提供的对称密码进行加密。 IV 字段将存储用于加密的初始化向量。存储要求取决于使用的密码和模式;



加密



加密和模式 / p>

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



加密密钥



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

  $ key_size = 32; // 256 bits 
$ encryption_key = openssl_random_pseudo_bytes($ key_size,$ strong);
//如果密钥是加密安全的,则$ true将是真的

一次或多次(如果您希望创建一系列加密密钥)。



初始化向量增加了随机性到加密和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),//填充数据
'AES-256-CBC',密码和模式
$ encryption_key, /密钥
0,//选项(不使用)
$ iv //初始化向量
);



存储要求



加密输出,像IV,是二进制;将这些值存储在数据库中可以通过使用诸如 BINARY VARBINARY 的指定列类型来完成。



输出值,如IV,是二进制;要在MySQL中存储这些值,请考虑使用 BINARY VARBINARY 列。如果这不是一个选项,您还可以使用 base64_encode() bin2hex() ,这样做需要33%更多存储空间。



解密



解密存储的值类似:

  function pkcs7_unpad($ data)
{
return substr($ data,0,-ord($ data [strlen $ data) - 1]));
}

$ row = $ result-> fetch(PDO :: FETCH_ASSOC); //从数据库读取结果
// $ 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
));



认证加密



通过附加从密钥(不同于加密密钥)和密文生成的签名来生成密文的完整性。在密码文本被解密之前,首先验证签名(优选地使用恒定时间比较方法)。



示例


$ b b

  //生成一次,保证安全
$ auth_key = openssl_random_pseudo_bytes(32,$ strong);

//身份验证
$ auth = hash_hmac('sha256',$ enc_name,$ auth_key,true);
$ auth_enc_name = $ auth。 $ enc_name;

//验证
$ 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)){
//执行解密
}

另请参阅: hash_equals()



散列



必须尽可能避免在数据库中存储可逆密码;您只希望验证密码,而不是知道其内容。如果用户丢失密码,最好让他们重置,而不是向他们发送原来的密码(确保密码重置只能在有限的时间内完成)。



应用哈希函数是一种单向运算;之后可以安全地用于验证而不暴露原始数据;对于密码,强力方法是一种可行的方法,由于其相对较短的长度和许多人的密码选择差,因此它是一种可行的方法。



散列算法如MD5或SHA1以相对于已知的散列值来验证文件内容。它们经过了极大的优化,可以在尽可能快地完成验证的同时保持准确性。考虑到它们相对有限的输出空间,很容易用已知的密码和它们各自的哈希输出,彩虹表来构建数据库。



在哈希值之前添加一个salt到密码会使彩虹表无用,但最近的硬件进步使得强力查找成为可行的方法。这就是为什么你需要一个哈希算法,故意慢,根本不可能优化。它还应该能够增加更快速硬件的负载,而不影响验证现有密码哈希值以使其成为未来证明的能力。



目前有两种流行的选择:


  1. PBKDF2(基于密码的密钥导出功能v2)

  2. bcrypt(aka Blowfish)

此回答将使用bcrypt的示例。



strong>



密码哈希可以生成如下:

  $ 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() code> [A-Za-z0-9 /.]

匹配所需的字母code> strtr()

crypt() 函数执行基于算法的哈希算法(对于Blowfish的 $ 2y $ ),成本因子(在3GHz机器上,13的系数大约为0.40s),以及22个字符的盐。


$ b

验证



一旦获取了包含用户信息的行,您以这种方式验证密码:

  $ given_password = $ _POST ['password']; //提交的密码
$ db_hash = $ row ['Password']; //字段与密码哈希

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

if(isEqual($ given_hash,$ db_hash)){
//用户密码验证
}

//常量时间字符串比较
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值。如果给定的密码与散列匹配,则返回值产生相同的散列。



使用PHP 5.5的密码散列



PHP 5.5引入了密码散列函数您可以使用简化上述哈希方法:

  $ hash = password_hash($ password,PASSWORD_BCRYPT, => 13]); 

并验证:

  if(password_verify($ given_password,$ db_hash)){
// password valid
}

另请参阅: password_hash() password_verify()


I'm currently a student and I'm studying PHP, I'm trying to make a simple encrypt/decrypt of data in PHP. I made some online research and some of them were quite confusing(at least for me).

Here's what I'm trying to do:

I have a table consisting of these fields (UserID,Fname,Lname,Email,Password)

What I want to have is have the all fields encrypted and then be decrypted(Is it possible to use sha256 for encryption/decryption, if not any encryption algorithm)

Another thing I want to learn is how to create a one way hash(sha256) combined with a good "salt". (Basically I just want to have a simple implementation of encryption/decryption, hash(sha256)+salt) Sir/Ma'am, your answers would be of great help and be very much appreciated. Thank you++

解决方案

Foreword

Starting with your table definition:

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

Here are the changes:

  1. The fields Fname, Lname and Email will be encrypted using a symmetric cipher, provided by OpenSSL,
  2. The IV field will store the initialisation vector used for encryption. The storage requirements depend on the cipher and mode used; more about this later.
  3. The Password field will be hashed using a one-way password hash,

Encryption

Cipher and mode

Choosing the best encryption cipher and mode is beyond the scope of this answer, but the final choice affects the size of both the encryption key and initialisation vector; for this post we will be using AES-256-CBC which has a fixed block size of 16 bytes and a key size of either 16, 24 or 32 bytes.

Encryption key

A good encryption key is a binary blob that's generated from a reliable random number generator. The following example would be recommended (>= 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

This can be done once or multiple times (if you wish to create a chain of encryption keys). Keep these as private as possible.

IV

The initialisation vector adds randomness to the encryption and required for CBC mode. These values should be ideally be used only once (technically once per encryption key), so an update to any part of a row should regenerate it.

A function is provided to help you generate the IV:

$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);

Example

Let's encrypt the name field, using the earlier $encryption_key and $iv; to do this, we have to pad our data to the block size:

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
);

Storage requirements

The encrypted output, like the IV, is binary; storing these values in a database can be accomplished by using designated column types such as BINARY or VARBINARY.

The output value, like the IV, is binary; to store those values in MySQL, consider using BINARY or VARBINARY columns. If this is not an option, you can also convert the binary data into a textual representation using base64_encode() or bin2hex(), doing so requires between 33% to 100% more storage space.

Decryption

Decryption of the stored values is similar:

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
));

Authenticated encryption

You can further improve the integrity of the generated cipher text by appending a signature that's generated from a secret key (different from the encryption key) and the cipher text. Before the cipher text is decrypted, the signature is first verified (preferably with a constant-time comparison method).

Example

// 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
}

See also: hash_equals()

Hashing

Storing a reversible password in your database must be avoided as much as possible; you only wish to verify the password rather than knowing its contents. If a user loses their password, it's better to allow them to reset it rather than sending them their original one (make sure that password reset can only be done for a limited time).

Applying a hash function is a one-way operation; afterwards it can be safely used for verification without revealing the original data; for passwords, a brute force method is a feasible approach to uncover it due to its relatively short length and poor password choices of many people.

Hashing algorithms such as MD5 or SHA1 were made to verify file contents against a known hash value. They're greatly optimized to make this verification as fast as possible while still being accurate. Given their relatively limited output space it was easy to build a database with known passwords and their respective hash outputs, the rainbow tables.

Adding a salt to the password before hashing it would render a rainbow table useless, but recent hardware advancements made brute force lookups a viable approach. That's why you need a hashing algorithm that's deliberately slow and simply impossible to optimize. It should also be able to increase the load for faster hardware without affecting the ability to verify existing password hashes to make it future proof.

Currently there are two popular choices available:

  1. PBKDF2 (Password Based Key Derivation Function v2)
  2. bcrypt (aka Blowfish)

This answer will use an example with bcrypt.

Generation

A password hash can be generated like this:

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

The salt is generated with openssl_random_pseudo_bytes() to form a random blob of data which is then run through base64_encode() and strtr() to match the required alphabet of [A-Za-z0-9/.].

The crypt() function performs the hashing based on the algorithm ($2y$ for Blowfish), the cost factor (a factor of 13 takes roughly 0.40s on a 3GHz machine) and the salt of 22 characters.

Validation

Once you have fetched the row containing the user information, you validate the password in this manner:

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

To verify a password, you call crypt() again but you pass the previously calculated hash as the salt value. The return value yields the same hash if the given password matches the hash. To verify the hash, it's often recommended to use a constant-time comparison function to avoid timing attacks.

Password hashing with PHP 5.5

PHP 5.5 introduced the password hashing functions that you can use to simplify the above method of hashing:

$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);

And verifying:

if (password_verify($given_password, $db_hash)) {
    // password valid
}

See also: password_hash(), password_verify()

这篇关于如何加密/解密数据在PHP?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆