你如何加密和解密PHP字符串?

时间:2013-05-17 02:57:14

标签: php security encryption cryptography encryption-symmetric

我的意思是:

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

可能是这样的:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • 在PHP中,你怎么能这样做?

尝试使用 Crypt_Blowfish ,但它对我不起作用。

10 个答案:

答案 0 :(得分:364)

在您采取进一步措施之前,请先了解the difference between encryption and authentication,以及为什么您可能需要经过身份验证的加密,而不仅仅是加密

要实施经过身份验证的加密,您需要加密然后加密MAC。 The order of encryption and authentication is very important!这个问题的现有答案之一就是犯了这个错误;就像许多用PHP编写的加密库一样。

您应该avoid implementing your own cryptography,而是使用由加密专家编写并审核的安全库。

  

更新:PHP 7.2现在提供了libsodium !为了获得最佳安全性,请将系统更新为使用PHP 7.2或更高版本,并且只遵循本答案中的libsodium建议。

Use libsodium if you have PECL access(或sodium_compat如果你想要没有PECL的libsodium);否则...
Use defuse/php-encryption;不要推出自己的加密技术!

上面链接的两个库都可以轻松,轻松地将经过身份验证的加密实现到您自己的库中。

如果您仍然希望编写和部署自己的加密库,而不是互联网上每位加密专家的传统智慧,那么这些是您必须采取的步骤。

加密:

  1. 在点击率模式下使用AES进行加密。您也可以使用GCM(不需要单独的MAC)。此外,ChaCha20和Salsa20(由libsodium提供)是流密码,不需要特殊模式。
  2. 除非您选择上面的GCM,否则您应该使用HMAC-SHA-256验证密文(或者,对于流密码,Poly1305 - 大多数libsodium API都会为您执行此操作)。 MAC应涵盖IV和密文!
  3. 解密:

    1. 除非使用Poly1305或GCM,否则重新计算密文的MAC并将其与使用hash_equals()发送的MAC进行比较。如果失败,则中止。
    2. 解密消息。
    3. 其他设计注意事项:

      1. 不要压缩任何东西。密文不可压缩;在加密之前压缩明文会导致信息泄露(例如,TL​​S上的CRIME和BREACH)。
      2. 请确保使用mb_strlen()mb_substr(),使用'8bit'字符集模式来阻止mbstring.func_overload问题。
      3. IVs应该使用CSPRNG生成;如果您正在使用mcrypt_create_iv()请勿使用MCRYPT_RAND
      4. 除非您使用AEAD构造,ALWAYS encrypt then MAC!
      5. bin2hex()base64_encode()等可能会通过缓存时序泄漏有关加密密钥的信息。如果可能,请避免使用它们。
      6. 即使你遵循这里给出的建议,密码学也会出现很多问题。 始终让加密专家审核您的实施情况。如果您不幸与当地大学的加密学生成为私人朋友,您可以随时到Cryptography Stack Exchange论坛寻求建议。< / p>

        如果您需要对您的实施进行专业分析,您可以随时雇用reputable team of security consultants to review your PHP cryptography code(披露:我的雇主)。

        重要提示:何时不使用加密

        <强> Don't encrypt passwords 即可。您希望使用以下密码散列算法之一散列它们:

        切勿使用通用哈希函数(MD5,SHA256)进行密码存储。

        Don't encrypt URL Parameters这是错误的工具。

        使用Libsodium

        的PHP字符串加密示例

        如果您使用的是PHP&lt; 7.2或者没有安装libsodium,你可以使用sodium_compat来完成相同的结果(尽管速度较慢)。

        <?php
        declare(strict_types=1);
        
        /**
         * Encrypt a message
         * 
         * @param string $message - message to encrypt
         * @param string $key - encryption key
         * @return string
         * @throws RangeException
         */
        function safeEncrypt(string $message, string $key): string
        {
            if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
                throw new RangeException('Key is not the correct size (must be 32 bytes).');
            }
            $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        
            $cipher = base64_encode(
                $nonce.
                sodium_crypto_secretbox(
                    $message,
                    $nonce,
                    $key
                )
            );
            sodium_memzero($message);
            sodium_memzero($key);
            return $cipher;
        }
        
        /**
         * Decrypt a message
         * 
         * @param string $encrypted - message encrypted with safeEncrypt()
         * @param string $key - encryption key
         * @return string
         * @throws Exception
         */
        function safeDecrypt(string $encrypted, string $key): string
        {   
            $decoded = base64_decode($encrypted);
            $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
            $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
        
            $plain = sodium_crypto_secretbox_open(
                $ciphertext,
                $nonce,
                $key
            );
            if (!is_string($plain)) {
                throw new Exception('Invalid MAC');
            }
            sodium_memzero($ciphertext);
            sodium_memzero($key);
            return $plain;
        }
        

        然后测试一下:

        <?php
        // This refers to the previous code block.
        require "safeCrypto.php"; 
        
        // Do this once then store it somehow:
        $key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
        $message = 'We are all living in a yellow submarine';
        
        $ciphertext = safeEncrypt($message, $key);
        $plaintext = safeDecrypt($ciphertext, $key);
        
        var_dump($ciphertext);
        var_dump($plaintext);
        

        Halite - Libsodium Made Easier

        我正在研究的一个项目是名为Halite的加密库,旨在使libsodium更容易,更直观。

        <?php
        use \ParagonIE\Halite\KeyFactory;
        use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;
        
        // Generate a new random symmetric-key encryption key. You're going to want to store this:
        $key = new KeyFactory::generateEncryptionKey();
        // To save your encryption key:
        KeyFactory::save($key, '/path/to/secret.key');
        // To load it again:
        $loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');
        
        $message = 'We are all living in a yellow submarine';
        $ciphertext = SymmetricCrypto::encrypt($message, $key);
        $plaintext = SymmetricCrypto::decrypt($ciphertext, $key);
        
        var_dump($ciphertext);
        var_dump($plaintext);
        

        所有底层加密都由libsodium处理。

        使用defuse / php-encryption的示例

        <?php
        /**
         * This requires https://github.com/defuse/php-encryption
         * php composer.phar require defuse/php-encryption
         */
        
        use Defuse\Crypto\Crypto;
        use Defuse\Crypto\Key;
        
        require "vendor/autoload.php";
        
        // Do this once then store it somehow:
        $key = Key::createNewRandomKey();
        
        $message = 'We are all living in a yellow submarine';
        
        $ciphertext = Crypto::encrypt($message, $key);
        $plaintext = Crypto::decrypt($ciphertext, $key);
        
        var_dump($ciphertext);
        var_dump($plaintext);
        

        注意Crypto::encrypt()返回十六进制编码的输出。

        加密密钥管理

        如果您想要使用&#34;密码&#34;,请立即停止。您需要一个随机的128位加密密钥,而不是人类难忘的密码。

        您可以存储加密密钥以供长期使用,如下所示:

        $storeMe = bin2hex($key);
        

        并且,根据需要,您可以像这样检索它:

        $key = hex2bin($storeMe);
        

        强烈建议只存储随机生成的密钥以供长期使用,而不是将任何密码作为密钥(或导出密钥)。

        如果你正在使用Defuse的库:

        &#34;但我确实想要使用密码。&#34;

        这是一个坏主意,但没关系,这里有如何安全地做到这一点。

        首先,生成一个随机密钥并将其存储在常量中。

        /**
         * Replace this with your own salt! 
         * Use bin2hex() then add \x before every 2 hex characters, like so:
         */
        define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");
        

        请注意,您正在添加额外的工作,并且可以使用此常量作为键,并为您节省很多心痛!

        然后使用PBKDF2(如此)从您的密码中获取合适的加密密钥,而不是直接使用您的密码进行加密。

        /**
         * Get an AES key from a static password and a secret salt
         * 
         * @param string $password Your weak password here
         * @param int $keysize Number of bytes in encryption key
         */
        function getKeyFromPassword($password, $keysize = 16)
        {
            return hash_pbkdf2(
                'sha256',
                $password,
                MY_PBKDF2_SALT,
                100000, // Number of iterations
                $keysize,
                true
            );
        }
        

        不要只使用16个字符的密码。您的加密密钥将被完全破坏。

答案 1 :(得分:48)

我已经迟到了,但是在寻找正确的方法时,我遇到了这个页面,这是谷歌搜索的最高回报之一,所以我想分享一下我对这个问题的看法,我认为在撰写本文(2017年初)时它是最新的。从PHP 7.1.0起,mcrypt_decryptmcrypt_encrypt将被弃用,因此构建未来验证代码应使用openssl_encryptopenssl_decrypt

您可以执行以下操作:

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);
  

重要:这使用ECB mode,这是不安全的。如果您想要一个简单的解决方案而不参加加密工程中的速成课程,请不要自己编写,只需use a library

您也可以使用任何其他削片机方法,具体取决于您的安全需求。要了解可用的削片机方法,请参阅openssl_get_cipher_methods功能。

答案 2 :(得分:44)

什么不该做

  

警告:
  这个答案使用ECB。 ECB不是加密模式,它只是一个构建块。如本答案中所示,使用ECB实际上并不安全地加密字符串。不要在代码中使用ECB。请参阅Scott's answer以获得一个好的解决方案。

我得到了自己。实际上我在谷歌找到了一些答案,只是修改了一些东西。 然而,结果完全不安全。

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>

答案 3 :(得分:12)

对于Laravel框架

如果您使用的是Laravel框架,那么使用内部函数加密和解密会更容易。

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);
  

注意:请务必在中设置16个,24个或32个字符的随机字符串   config / app.php文件的关键选项。否则,加密值   不会安全。

答案 4 :(得分:6)

  

历史记录注释:这是在PHP4时编写的。这就是我们现在所说的“遗留代码”。

我已将这个答案留给了历史目的 - 但现在有些方法已被弃用,DES加密方法不是推荐做法等等。

我没有更新此代码有两个原因:1)我不再使用PHP中的加密方法,2)此代码仍然用于其目的:展示如何最小化,简单化的概念加密可以在PHP中工作。

如果您发现类似的简单,“PHP加密傻瓜”类型的来源,可以让人们开始使用10-20行或更少的代码,请在评论中告诉我。

除此之外,请享受早期PHP4简约加密答案的经典剧集。


理想情况下,您已经 - 或者可以获得 - 访问mcrypt PHP库,因为它肯定非常受欢迎且非常有用的各种任务。以下是不同类型的加密和一些示例代码:Encryption Techniques in PHP

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

一些警告:

1)当单向散列执行时,切勿使用可逆或“对称”加密。

2)如果数据真正敏感,如信用卡或社会安全号码,请停止;你需要的不仅仅是任何简单的代码块,而是需要一个专为此目的而设计的加密库,并且需要大量的时间来研究必要的方法。此外,软件加密可能是敏感数据安全性的10%。这就像重新布线核电站 - 接受任务是危险和困难的,并且如果是这样的话,超出了你的知识范围。经济处罚可能是巨大的,因此最好使用服务并将责任运送给他们。

3)如此处所列,任何类型的易于实施的加密都可以合理地保护您想要避免的轻微重要信息,或者在意外/故意泄漏的情况下限制暴露。但是看看密钥是如何以纯文本形式存储在Web服务器上的,如果他们可以获取数据,他们就可以获得解密密钥。

尽管如此,玩得开心:)

答案 5 :(得分:4)

这些是使用 AES256 CBC 使用PHP加密/解密字符串的紧凑方法:

function encryptString($plaintext, $password, $encoding = null) {
    $iv = openssl_random_pseudo_bytes(16);
    $ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
    return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}

function decryptString($ciphertext, $password, $encoding = null) {
    $ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
    if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
    return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}

用法:

$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");

答案 6 :(得分:3)

如果您不想使用库(您应该使用),那么请使用类似的内容(PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);

答案 7 :(得分:3)

已更新

PHP 7就绪版本。它使用PHP openssl_encrypt中的OpenSSL Library函数。

class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&Q@$&*!@#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);

答案 8 :(得分:0)

下面的代码在php中适用于所有带有特殊字符的字符串

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);

答案 9 :(得分:0)

在PHP中,可以使用称为OpenSSL函数的加密扩展之一对字符串进行加密和解密。

openssl_encrypt()函数:openssl_encrypt()函数用于加密数据。

语法如下:

string openssl_encrypt(字符串$ data,字符串$ method,字符串$ key,$ options = 0,字符串$ iv,字符串$ tag = NULL,字符串$ aad,int $ tag_length = 16)

参数如下:

$ data::它包含需要加密的字符串或数据。

$ method::使用openssl_get_cipher_methods()函数采用加密方法。

$ key:。它拥有加密密钥。

$ options :它保存标志OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING的按位析取。

$ iv:它保存初始化向量,该初始化向量不是NULL。

$ tag::它包含使用AEAD密码模式(GCM或CCM)时通过引用传递的身份验证标签。

$ aad::它包含其他身份验证数据。

$ tag_length::它保存身份验证标签的长度。对于GCM模式,身份验证标签的长度在4到16之间。

返回值::成功时返回加密的字符串,失败时返回FALSE。

openssl_decrypt()函数openssl_decrypt()函数用于解密数据。

语法如下:

string openssl_decrypt(字符串$ data,字符串$ method,字符串$ key,int $ options = 0,字符串$ iv,字符串$ tag,字符串$ aad)

参数如下:

$ data::它包含需要加密的字符串或数据。

$ method::使用openssl_get_cipher_methods()函数采用加密方法。

$ key:。它拥有加密密钥。

$ options :它保存标志OPENSSL_RAW_DATA和OPENSSL_ZERO_PADDING的按位析取。

$ iv:它保存初始化向量,该初始化向量不是NULL。

$ tag::它使用AEAD密码模式(GCM或CCM)保存身份验证标签。身份验证失败时,openssl_decrypt()将返回FALSE。

$ aad::它包含其他身份验证数据。

返回值::成功返回解密的字符串,失败则返回FALSE。

方法:首先声明一个字符串并将其存储到变量中,然后使用openssl_encrypt()函数加密给定的字符串,然后使用openssl_decrypt()函数解密给定的字符串。

您可以在以下位置找到示例: https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-a-php-string/