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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10916284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 23:21:08  来源:igfitidea点击:

How to encrypt/decrypt data in php?

phpsecurityencryptioncryptographyencryption-symmetric

提问by Randel Ramirez

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

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

Here's what I'm trying to do:

这是我想要做的:

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

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

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

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

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++

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

回答by Ja?ck

Foreword

前言

Starting with your table definition:

从您的表定义开始:

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

Here are the changes:

以下是变化:

  1. The fields Fname, Lnameand Emailwill be encrypted using a symmetric cipher, provided by OpenSSL,
  2. The IVfield will store the initialisation vectorused for encryption. The storage requirements depend on the cipher and mode used; more about this later.
  3. The Passwordfield will be hashed using a one-waypassword hash,
  1. 的字段FnameLnameEmail将使用对称密码,通过提供待加密的OpenSSL
  2. IV字段将存储用于加密的初始化向量。存储要求取决于使用的密码和模式;稍后会详细介绍这一点。
  3. Password字段将使用单向密码散列进行散列,

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.

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

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

一个好的加密密钥是由可靠的随机数生成器生成的二进制 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

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.

初始化向量为加密增加了随机性,这是 CBC 模式所必需的。理想情况下,这些值应该只使用一次(技术上每个加密密钥一次),因此对行的任何部分的更新都应该重新生成它。

A function is provided to help you generate the IV:

提供了一个函数来帮助您生成 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_keyand $iv; to do this, we have to pad our data to the block size:

让我们加密 name 字段,使用之前的$encryption_key$iv; 为此,我们必须将数据填充到块大小:

function pkcs7_pad($data, $size)
{
    $length = $size - strlen($data) % $size;
    return $data . str_repeat(chr($length), $length);
}

$name = 'Hyman';
$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 BINARYor VARBINARY.

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

The output value, like the IV, is binary; to store those values in MySQL, consider using BINARYor VARBINARYcolumns. 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.

输出值和 IV 一样,是二进制的;要将这些值存储在 MySQL 中,请考虑使用BINARYVARBINARY列。如果这不是一个选项,您还可以使用base64_encode()或将二进制数据转换为文本表示bin2hex(),这样做需要多出 33% 到 100% 的存储空间。

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

也可以看看: 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.

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

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)
  1. PBKDF2(基于密码的密钥派生函数 v2)
  2. bcrypt(又名河豚)

This answer will use an example with bcrypt.

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

Generation

一代

A password hash can be generated like this:

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

$password = 'my password';
$random = openssl_random_pseudo_bytes(18);
$salt = sprintf('y$%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/.].

生成盐openssl_random_pseudo_bytes()以形成随机数据块,然后运行该数据块base64_encode()strtr()匹配所需的[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.

crypt()函数根据算法($2y$对于 Blowfish)、成本因子(13 的因子在 3GHz 机器上大约需要 0.40 秒)和 22 个字符的盐来执行散列。

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.

要验证密码,您crypt()再次调用,但将先前计算的哈希值作为盐值传递。如果给定的密码与散列匹配,则返回值产生相同的散列。为了验证散列,通常建议使用恒定时间比较函数来避免时序攻击。

Password hashing with PHP 5.5

使用 PHP 5.5 进行密码散列

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

PHP 5.5 引入了密码散列函数,可用于简化上述散列方法:

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

另请参阅:password_hash()password_verify()

回答by romo

I'm think this has been answered before...but anyway, if you want to encrypt/decrypt data, you can't use SHA256

我认为这之前已经回答过......但无论如何,如果你想加密/解密数据,你不能使用 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);

回答by cytinus

Answer Background and Explanation

回答背景及说明

To understand this question, you must first understand what SHA256 is. SHA256 is a Cryptographic Hash Function. A Cryptographic Hash Function is a one-way function, whose output is cryptographically secure. This means it is easy to compute a hash (equivalent to encrypting data), but hard to get the original input using the hash (equivalent to decrypting the data). Since using a Cryptographic hash function means decrypting is computationally infeasible, so therefore you cannot perform decryption with SHA256.

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

What you want to use is a two-way function, but more specifically, a Block Cipher. A function that allows for both encryption and decryption of data. The functions mcrypt_encryptand mcrypt_decryptby default use the Blowfish algorithm. PHP's use of mcrypt can be found in this manual. A list of cipher definitionsto select the cipher mcrypt uses also exists. A wiki on Blowfish can be found at Wikipedia. A block cipher encrypts the input in blocks of known size and position with a known key, so that the data can later be decrypted using the key. This is what SHA256 cannot provide you.

您要使用的是双向函数,但更具体地说,是一个Block Cipher。允许对数据进行加密和解密的功能。这些函数mcrypt_encryptmcrypt_decrypt默认情况下使用 Blowfish 算法。PHP 对 mcrypt 的使用可以在本手册中找到。还存在用于选择密码 mcrypt 使用的密码定义列表。可以在Wikipedia上找到关于 Blowfish 的 wiki 。分组密码使用已知密钥对已知大小和位置的块中的输入进行加密,以便稍后可以使用该密钥对数据进行解密。这是 SHA256 无法为您提供的。

Code

代码

$key = 'ThisIsTheCipherKey';

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

$plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);

回答by Vivek

Here is an example using openssl_encrypt

这是一个使用 openssl_encrypt 的例子

//Encryption:
$textToEncrypt = "My Text to Encrypt";
$encryptionMethod = "AES-256-CBC";
$secretHash = "encryptionhash";
$iv = mcrypt_create_iv(16, MCRYPT_RAND);
$encryptedText = openssl_encrypt($textToEncrypt,$encryptionMethod,$secretHash, 0, $iv);

//Decryption:
$decryptedText = openssl_decrypt($encryptedText, $encryptionMethod, $secretHash, 0, $iv);
print "My Decrypted Text: ". $decryptedText;

回答by gauravbhai daxini

     function my_simple_crypt( $string, $action = 'e' ) {
        // you may change these values to your own
        $secret_key = 'my_simple_secret_key';
        $secret_iv = 'my_simple_secret_iv';

        $output = false;
        $encrypt_method = "AES-256-CBC";
        $key = hash( 'sha256', $secret_key );
        $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );

        if( $action == 'e' ) {
            $output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
        }
        else if( $action == 'd' ){
            $output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
        }

        return $output;
    }

回答by Kai Noack

It took me quite a while to figure out, how to not get a falsewhen using openssl_decrypt()and get encrypt and decrypt working.

我花了很长时间才弄清楚,如何false在使用openssl_decrypt()和加密和解密工作时没有得到一个。

    // cryptographic key of a binary string 16 bytes long (because AES-128 has a key size of 16 bytes)
    $encryption_key = '58adf8c78efef9570c447295008e2e6e'; // example
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    $encrypted = openssl_encrypt($plaintext, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, $iv);
    $encrypted = $encrypted . ':' . base64_encode($iv);

    // decrypt to get again $plaintext
    $parts = explode(':', $encrypted);
    $decrypted = openssl_decrypt($parts[0], 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, base64_decode($parts[1])); 

If you want to pass the encrypted string via a URL, you need to urlencode the string:

如果要通过 URL 传递加密字符串,则需要对字符串进行 urlencode:

    $encrypted = urlencode($encrypted);

To better understand what is going on, read:

为了更好地了解正在发生的事情,请阅读:

To generate 16 bytes long keys you can use:

要生成 16 字节长的密钥,您可以使用:

    $bytes = openssl_random_pseudo_bytes(16);
    $hex = bin2hex($bytes);

To see error messages of openssl you can use: echo openssl_error_string();

要查看 openssl 的错误消息,您可以使用: echo openssl_error_string();

Hope that helps.

希望有帮助。