How do you Encrypt and Decrypt a PHP String?

后端 未结 10 1544
梦毁少年i
梦毁少年i 2020-11-22 01:21

What I mean is:

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

May

10条回答
  •  北恋
    北恋 (楼主)
    2020-11-22 01:40

    Before you do anything further, seek to understand the difference between encryption and authentication, and why you probably want authenticated encryption rather than just encryption.

    To implement authenticated encryption, you want to Encrypt then MAC. The order of encryption and authentication is very important! One of the existing answers to this question made this mistake; as do many cryptography libraries written in PHP.

    You should avoid implementing your own cryptography, and instead use a secure library written by and reviewed by cryptography experts.

    Update: PHP 7.2 now provides libsodium! For best security, update your systems to use PHP 7.2 or higher and only follow the libsodium advice in this answer.

    Use libsodium if you have PECL access (or sodium_compat if you want libsodium without PECL); otherwise...
    Use defuse/php-encryption; don't roll your own cryptography!

    Both of the libraries linked above make it easy and painless to implement authenticated encryption into your own libraries.

    If you still want to write and deploy your own cryptography library, against the conventional wisdom of every cryptography expert on the Internet, these are the steps you would have to take.

    Encryption:

    1. Encrypt using AES in CTR mode. You may also use GCM (which removes the need for a separate MAC). Additionally, ChaCha20 and Salsa20 (provided by libsodium) are stream ciphers and do not need special modes.
    2. Unless you chose GCM above, you should authenticate the ciphertext with HMAC-SHA-256 (or, for the stream ciphers, Poly1305 -- most libsodium APIs do this for you). The MAC should cover the IV as well as the ciphertext!

    Decryption:

    1. Unless Poly1305 or GCM is used, recalculate the MAC of the ciphertext and compare it with the MAC that was sent using hash_equals(). If it fails, abort.
    2. Decrypt the message.

    Other Design Considerations:

    1. Do not compress anything ever. Ciphertext is not compressible; compressing plaintext before encryption can lead to information leaks (e.g. CRIME and BREACH on TLS).
    2. Make sure you use mb_strlen() and mb_substr(), using the '8bit' character set mode to prevent mbstring.func_overload issues.
    3. IVs should be generating using a CSPRNG; If you're using mcrypt_create_iv(), DO NOT USE MCRYPT_RAND!
      • Also check out random_compat.
    4. Unless you're using an AEAD construct, ALWAYS encrypt then MAC!
    5. bin2hex(), base64_encode(), etc. may leak information about your encryption keys via cache timing. Avoid them if possible.

    Even if you follow the advice given here, a lot can go wrong with cryptography. Always have a cryptography expert review your implementation. If you are not fortunate enough to be personal friends with a cryptography student at your local university, you can always try the Cryptography Stack Exchange forum for advice.

    If you need a professional analysis of your implementation, you can always hire a reputable team of security consultants to review your PHP cryptography code (disclosure: my employer).

    Important: When to Not Use Encryption

    Don't encrypt passwords. You want to hash them instead, using one of these password-hashing algorithms:

    • Argon2
    • scrypt
    • bcrypt
    • PBKDF2-SHA256 with 86,000 iterations

    Never use a general-purpose hash function (MD5, SHA256) for password storage.

    Don't encrypt URL Parameters. It's the wrong tool for the job.

    PHP String Encryption Example with Libsodium

    If you are on PHP < 7.2 or otherwise do not have libsodium installed, you can use sodium_compat to accomplish the same result (albeit slower).

    Then to test it out:

    Halite - Libsodium Made Easier

    One of the projects I've been working on is an encryption library called Halite, which aims to make libsodium easier and more intuitive.

    All of the underlying cryptography is handled by libsodium.

    Example with defuse/php-encryption

    Note: Crypto::encrypt() returns hex-encoded output.

    Encryption Key Management

    If you're tempted to use a "password", stop right now. You need a random 128-bit encryption key, not a human memorable password.

    You can store an encryption key for long-term use like so:

    $storeMe = bin2hex($key);
    

    And, on demand, you can retrieve it like so:

    $key = hex2bin($storeMe);
    

    I strongly recommend just storing a randomly generated key for long-term use instead of any sort of password as the key (or to derive the key).

    If you're using Defuse's library:

    • $string = $keyObject->saveToAsciiSafeString()
    • $loaded = Key::loadFromAsciiSafeString($string);

    "But I really want to use a password."

    That's a bad idea, but okay, here's how to do it safely.

    First, generate a random key and store it in a constant.

    /**
     * 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");
    

    Note that you're adding extra work and could just use this constant as the key and save yourself a lot of heartache!

    Then use PBKDF2 (like so) to derive a suitable encryption key from your password rather than encrypting with your password directly.

    /**
     * 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
        );
    }
    

    Don't just use a 16-character password. Your encryption key will be comically broken.

提交回复
热议问题