복붙노트

PHP를 사용한 가장 간단한 양방향 암호화

PHP

PHP를 사용한 가장 간단한 양방향 암호화

일반적인 PHP 설치에서 양방향 암호화를 수행하는 가장 간단한 방법은 무엇입니까?

나는 문자열 키로 데이터를 암호화 할 수 있어야하고, 반대쪽에서 같은 키를 사용하여 해독 할 수 있어야한다.

보안은 코드의 이식성만큼 큰 문제가 아니므로 가능한 한 간단하게 유지할 수 있기를 바랍니다. 현재 RC4 구현을 사용하고 있지만 기본적으로 지원되는 것을 찾을 수 있으면 많은 불필요한 코드를 절약 할 수 있습니다.

해결법

  1. ==============================

    1.편집 됨 :

    편집 됨 :

    실제로 openssl_encrypt () 및 openssl_decrypt ()를 사용해야합니다.

    Scott이 말했듯이, Mcrypt는 2007 년 이래로 업데이트되지 않았기 때문에 좋은 생각이 아닙니다.

    PHP에서 Mcrypt를 제거하는 RFC도 있습니다 - https://wiki.php.net/rfc/mcrypt-viking-funeral

  2. ==============================

    2.중요 : 매우 특별한 유스 케이스가 아니라면 암호를 암호화하지 말고 대신 암호 해시 알고리즘을 사용하십시오. 누군가가 서버 측 응용 프로그램에서 암호를 암호화한다고 말하면 그들은 정보가 없거나 위험한 시스템 설계를 설명하고 있습니다. 안전하게 암호를 저장하는 것은 암호화와는 완전히 별개의 문제입니다.

    중요 : 매우 특별한 유스 케이스가 아니라면 암호를 암호화하지 말고 대신 암호 해시 알고리즘을 사용하십시오. 누군가가 서버 측 응용 프로그램에서 암호를 암호화한다고 말하면 그들은 정보가 없거나 위험한 시스템 설계를 설명하고 있습니다. 안전하게 암호를 저장하는 것은 암호화와는 완전히 별개의 문제입니다.

    정보를 얻으십시오. 안전한 시스템 설계.

    PHP 5.4 이상을 사용하고 있고 암호 모듈을 직접 작성하고 싶지 않은 경우 인증 된 암호화를 제공하는 기존 라이브러리를 사용하는 것이 좋습니다. 필자가 링크 한 라이브러리는 PHP가 제공하는 것에 의존하고 있으며 소수의 보안 연구원이 정기적으로 검토하고 있습니다. (포함되어 있습니다.)

    귀하의 이식성 목표가 PECL 확장을 요구하는 것을 막지 않는다면, libsodium은 귀하가 또는 PHP로 작성할 수있는 모든 것에 대해 적극 권장됩니다.

    업데이트 (2016-06-12) : 이제 sodium_compat를 사용하고 PECL 확장을 설치하지 않고 동일한 암호화 libsodium을 사용할 수 있습니다.

    당신이 암호 공학에 손을 시험하고 싶다면 계속 읽어보십시오.

    먼저, 인증되지 않은 암호화와 Cryptographic Doom Principle의 위험성을 알아야합니다.

    PHP에서 암호화는 실제로 간단합니다 (정보를 암호화하는 방법에 대한 결정을 내리면 openssl_encrypt () 및 openssl_decrypt ()를 사용하게됩니다.) 시스템에서 지원되는 메소드 목록은 openssl_get_cipher_methods ()를 참조하십시오. 선택 항목은 CTR 모드의 AES입니다.

    현재 AES 키 크기가 걱정할 중요한 문제라고 믿을만한 이유가 없습니다 (256 비트 모드의 잘못된 키 스케줄링으로 인해 더 큰 것이 좋지 않을 수도 있습니다).

    참고 : 우리는 abandonware이고 보안에 영향을 줄 수있는 패치되지 않은 버그가있어 mcrypt를 사용하지 않습니다. 이러한 이유들 때문에 다른 PHP 개발자들도이를 피하도록 권장합니다.

    class UnsafeCrypto
    {
        const METHOD = 'aes-256-ctr';
    
        /**
         * Encrypts (but does not authenticate) a message
         * 
         * @param string $message - plaintext message
         * @param string $key - encryption key (raw binary expected)
         * @param boolean $encode - set to TRUE to return a base64-encoded 
         * @return string (raw binary)
         */
        public static function encrypt($message, $key, $encode = false)
        {
            $nonceSize = openssl_cipher_iv_length(self::METHOD);
            $nonce = openssl_random_pseudo_bytes($nonceSize);
    
            $ciphertext = openssl_encrypt(
                $message,
                self::METHOD,
                $key,
                OPENSSL_RAW_DATA,
                $nonce
            );
    
            // Now let's pack the IV and the ciphertext together
            // Naively, we can just concatenate
            if ($encode) {
                return base64_encode($nonce.$ciphertext);
            }
            return $nonce.$ciphertext;
        }
    
        /**
         * Decrypts (but does not verify) a message
         * 
         * @param string $message - ciphertext message
         * @param string $key - encryption key (raw binary expected)
         * @param boolean $encoded - are we expecting an encoded string?
         * @return string
         */
        public static function decrypt($message, $key, $encoded = false)
        {
            if ($encoded) {
                $message = base64_decode($message, true);
                if ($message === false) {
                    throw new Exception('Encryption failure');
                }
            }
    
            $nonceSize = openssl_cipher_iv_length(self::METHOD);
            $nonce = mb_substr($message, 0, $nonceSize, '8bit');
            $ciphertext = mb_substr($message, $nonceSize, null, '8bit');
    
            $plaintext = openssl_decrypt(
                $ciphertext,
                self::METHOD,
                $key,
                OPENSSL_RAW_DATA,
                $nonce
            );
    
            return $plaintext;
        }
    }
    
    $message = 'Ready your ammunition; we attack at dawn.';
    $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
    
    $encrypted = UnsafeCrypto::encrypt($message, $key);
    $decrypted = UnsafeCrypto::decrypt($encrypted, $key);
    
    var_dump($encrypted, $decrypted);
    

    데모 : https://3v4l.org/jl7qR

    위의 간단한 암호화 라이브러리는 여전히 사용하기에 안전하지 않습니다. 암호문을 해독하기 전에 암호문을 인증하고 확인해야합니다.

    참고 : 기본적으로 UnsafeCrypto :: encrypt ()는 원시 이진 문자열을 반환합니다. 이진 안전 형식 (base64 인코딩)으로 저장해야하는 경우 다음과 같이 호출하십시오.

    $message = 'Ready your ammunition; we attack at dawn.';
    $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
    
    $encrypted = UnsafeCrypto::encrypt($message, $key, true);
    $decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);
    
    var_dump($encrypted, $decrypted);
    

    데모 : http://3v4l.org/f5K93

    class SaferCrypto extends UnsafeCrypto
    {
        const HASH_ALGO = 'sha256';
    
        /**
         * Encrypts then MACs a message
         * 
         * @param string $message - plaintext message
         * @param string $key - encryption key (raw binary expected)
         * @param boolean $encode - set to TRUE to return a base64-encoded string
         * @return string (raw binary)
         */
        public static function encrypt($message, $key, $encode = false)
        {
            list($encKey, $authKey) = self::splitKeys($key);
    
            // Pass to UnsafeCrypto::encrypt
            $ciphertext = parent::encrypt($message, $encKey);
    
            // Calculate a MAC of the IV and ciphertext
            $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);
    
            if ($encode) {
                return base64_encode($mac.$ciphertext);
            }
            // Prepend MAC to the ciphertext and return to caller
            return $mac.$ciphertext;
        }
    
        /**
         * Decrypts a message (after verifying integrity)
         * 
         * @param string $message - ciphertext message
         * @param string $key - encryption key (raw binary expected)
         * @param boolean $encoded - are we expecting an encoded string?
         * @return string (raw binary)
         */
        public static function decrypt($message, $key, $encoded = false)
        {
            list($encKey, $authKey) = self::splitKeys($key);
            if ($encoded) {
                $message = base64_decode($message, true);
                if ($message === false) {
                    throw new Exception('Encryption failure');
                }
            }
    
            // Hash Size -- in case HASH_ALGO is changed
            $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
            $mac = mb_substr($message, 0, $hs, '8bit');
    
            $ciphertext = mb_substr($message, $hs, null, '8bit');
    
            $calculated = hash_hmac(
                self::HASH_ALGO,
                $ciphertext,
                $authKey,
                true
            );
    
            if (!self::hashEquals($mac, $calculated)) {
                throw new Exception('Encryption failure');
            }
    
            // Pass to UnsafeCrypto::decrypt
            $plaintext = parent::decrypt($ciphertext, $encKey);
    
            return $plaintext;
        }
    
        /**
         * Splits a key into two separate keys; one for encryption
         * and the other for authenticaiton
         * 
         * @param string $masterKey (raw binary)
         * @return array (two raw binary strings)
         */
        protected static function splitKeys($masterKey)
        {
            // You really want to implement HKDF here instead!
            return [
                hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
                hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
            ];
        }
    
        /**
         * Compare two strings without leaking timing information
         * 
         * @param string $a
         * @param string $b
         * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
         * @return boolean
         */
        protected static function hashEquals($a, $b)
        {
            if (function_exists('hash_equals')) {
                return hash_equals($a, $b);
            }
            $nonce = openssl_random_pseudo_bytes(32);
            return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
        }
    }
    
    $message = 'Ready your ammunition; we attack at dawn.';
    $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
    
    $encrypted = SaferCrypto::encrypt($message, $key);
    $decrypted = SaferCrypto::decrypt($encrypted, $key);
    
    var_dump($encrypted, $decrypted);
    

    데모 : 원시 바이너리, base64 인코딩

    프로덕션 환경에서이 SaferCrypto 라이브러리를 사용하거나 동일한 개념을 사용자가 구현하려는 경우, 먼저 거주 용 암호 작성자에게 연락하여 다른 의견을 제시 할 것을 강력히 권장합니다. 그들은 내가 알지도 못하는 실수에 대해 말할 수 있습니다.

    평판 좋은 암호 라이브러리를 사용하는 것이 훨씬 나을 것입니다.

  3. ==============================

    3.해당 매개 변수와 함께 mcrypt_encrypt () 및 mcrypt_decrypt ()를 사용하십시오. 정말 쉽고 간단합니다. 전투 테스트를 거친 암호화 패키지를 사용합니다.

    해당 매개 변수와 함께 mcrypt_encrypt () 및 mcrypt_decrypt ()를 사용하십시오. 정말 쉽고 간단합니다. 전투 테스트를 거친 암호화 패키지를 사용합니다.

    편집하다

    이 답변을받은 후 5 년 4 개월이 지나면 mcrypt 확장이 현재 PHP에서 비추천 및 최종 제거 중입니다.

  4. ==============================

    4.다음은 간단하지만 보안이 충분한 구현입니다.

    다음은 간단하지만 보안이 충분한 구현입니다.

    코드 및 예제는 https://stackoverflow.com/a/19445173/1387163에 있습니다.

  5. ==============================

    5.PHP 7.2는 Mcrypt에서 완전히 벗어 났으며 이제 암호화는 유지 보수가 가능한 Libsodium 라이브러리를 기반으로합니다.

    PHP 7.2는 Mcrypt에서 완전히 벗어 났으며 이제 암호화는 유지 보수가 가능한 Libsodium 라이브러리를 기반으로합니다.

    모든 암호화 요구 사항은 기본적으로 Libsodium 라이브러리를 통해 해결할 수 있습니다.

    // On Alice's computer:
    $msg = 'This comes from Alice.';
    $signed_msg = sodium_crypto_sign($msg, $secret_sign_key);
    
    
    // On Bob's computer:
    $original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);
    if ($original_msg === false) {
        throw new Exception('Invalid signature');
    } else {
        echo $original_msg; // Displays "This comes from Alice."
    }
    

    Libsodium 설명서 : https://github.com/paragonie/pecl-libsodium-doc

  6. ==============================

    6.키 또는 외부 라이브러리가없는 의사 단일 암호화 / 복호화를위한 매우 간단한 한 줄짜리 기본 방법 :

    키 또는 외부 라이브러리가없는 의사 단일 암호화 / 복호화를위한 매우 간단한 한 줄짜리 기본 방법 :

    이것은 일반적인 PHP 설치에서 양방향 (가짜 / 의사) 암호화를 수행하는 가장 간단한 방법입니다.

  7. from https://stackoverflow.com/questions/9262109/simplest-two-way-encryption-using-php by cc-by-sa and MIT license