[php] PHP 문자열을 어떻게 암호화하고 해독합니까?

내가 말하고 싶은 건:

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했지만 작동하지 않았습니다.



답변

추가 작업을 수행하기 전에 암호화인증 의 차이점 과 단순히 암호화가 아닌 인증 된 암호화를 원하는 이유 를 이해 하십시오 .

인증 된 암호화를 구현하려면 암호화 한 다음 MAC을 사용하려고합니다. 암호화 및 인증 순서는 매우 중요합니다! 이 질문에 대한 기존의 답변 중 하나가이 실수를 범했습니다. PHP로 작성된 많은 암호화 라이브러리와 마찬가지로.

당신은해야 자신의 암호화를 구현하지 않도록 하고, 대신 보안 라이브러리에 의해 작성하고 암호 전문가 검토를 사용합니다.

업데이트 : PHP 7.2는 이제 libsodium을 제공합니다 ! 최상의 보안을 위해 PHP 7.2 이상을 사용하도록 시스템을 업데이트하고이 답변의 libsodium 조언 만 따르십시오.

사용 libsodium 당신은 PECL에 액세스 할 수있는 경우 (또는 sodium_compat 당신이 PECL없이 libsodium을 원하는 경우); 그렇지 않으면 …
defuse / php-encryption 사용 ; 자신의 암호화를 굴리지 마십시오!

위에 링크 된 두 라이브러리 모두 인증 된 암호화를 자신의 라이브러리에 쉽고 쉽게 구현할 수 있습니다.

인터넷에있는 모든 암호화 전문가의 일반적인 지식에 반하여 자체 암호화 라이브러리를 작성하고 배포하려는 경우 다음 단계를 수행해야합니다.

암호화 :

  1. CTR 모드에서 AES를 사용하여 암호화합니다. GCM을 사용할 수도 있습니다 (별도의 MAC이 필요 없음). 또한 ChaCha20 및 Salsa20 (의해 제공 libsodium ) 스트림 암호이며, 특별한 모드가 필요하지 않습니다.
  2. 위에서 GCM을 선택하지 않은 경우 HMAC-SHA-256을 사용하여 암호문을 인증해야합니다 (또는 스트림 암호의 경우 Poly1305-대부분의 libsodium API가이를 수행함). MAC은 암호문뿐만 아니라 IV도 포함해야합니다!

복호화 :

  1. Poly1305 또는 GCM을 사용하지 않는 한 암호문의 MAC을 다시 계산하고이를 사용하여 보낸 MAC과 비교하십시오. hash_equals() . 실패하면 중단하십시오.
  2. 메시지를 해독하십시오.

다른 디자인 고려 사항 :

  1. 압축하지 마십시오. 암호문은 압축 할 수 없습니다. 암호화 전에 일반 텍스트를 압축하면 정보가 유출 될 수 있습니다 (예 : CRLS 및 BREACH on TLS).
  2. 당신이 사용 확인 mb_strlen()mb_substr()사용 '8bit'방지하기 위해 문자 설정 모드 mbstring.func_overload문제.
  3. IV는 CSPRNG를 사용하여 생성되어야합니다 . 당신이 사용하는 경우 mcrypt_create_iv(), 사용을하지 마십시오MCRYPT_RAND !
  4. AEAD 구문을 사용하지 않는 한 항상 MAC을 암호화 한 다음!
  5. bin2hex(), base64_encode(), 등 캐시 타이밍을 통해 암호화 키에 대한 정보가 누수 될 수 있습니다. 가능하면 피하십시오.

여기에 제공된 조언을 따를지라도 암호화에는 많은 문제가 발생할 수 있습니다. 암호 전문가가 항상 구현을 검토하도록하십시오. 지역 대학에서 암호화 학생과 개인적인 친구가 될만큼 운이 좋지 않은 경우 언제든지 암호화 스택 교환을 시도 할 수 있습니다 포럼에서 조언을 구할 수 있습니다.

구현에 대한 전문적인 분석이 필요한 경우, 평판이 좋은 보안 컨설턴트 팀을 고용 하여 PHP 암호화 코드 (공개 : 내 고용주) 를 검토 할 수 있습니다 .

중요 : 암호화를 사용하지 않는 경우

비밀번호를 암호화 하지 마십시오 . 당신이 원하는 해시 이러한 암호 해싱 알고리즘 중 하나를 사용하는 대신 그들 :

비밀번호 저장에 범용 해시 기능 (MD5, SHA256)을 사용하지 마십시오.

URL 매개 변수를 암호화하지 마십시오 . 작업에 대한 잘못된 도구입니다.

Libsodium을 사용한 PHP 문자열 암호화 예제

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

내가 작업했던 프로젝트 중 하나라는 암호화 라이브러리 암염 보다 쉽고 직관적 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()16 진수로 인코딩 된 출력을 반환합니다.

암호화 키 관리

“암호”를 사용하고 싶은 유혹이 있다면 지금 중지하십시오. 사람이 기억할 수있는 암호가 아닌 임의의 128 비트 암호화 키가 필요합니다.

다음과 같이 장기간 사용하기 위해 암호화 키를 저장할 수 있습니다.

$storeMe = bin2hex($key);

요청시 다음과 같이 검색 할 수 있습니다.

$key = hex2bin($storeMe);

내가 강하게 단지 장기간 사용을 위해 임의로 생성 된 키 대신 키와 암호를 모든 종류의를 저장하는 것이 좋습니다 (또는 키를 파생합니다).

Defuse의 라이브러리를 사용하는 경우 :

“하지만 정말 암호를 사용하고 싶습니다.”

좋지 않은 생각이지만, 안전하게하는 방법은 다음과 같습니다.

먼저 임의의 키를 생성하여 상수로 저장하십시오.

/**
 * 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 자 암호 만 사용하지 마십시오. 암호화 키가 엉망이됩니다.


답변

나는 파티에 늦었지만 올바른 방법을 찾고 있는데이 페이지를 방문한 것은 Google 검색 결과에서 가장 많이 나온 것 중 하나이므로 문제에 대한 견해를 공유하고 싶습니다. 이 게시물을 작성할 당시의 최신 정보 (2017 년 초). PHP 7.1.0로부터 mcrypt_decryptmcrypt_encrypt그래서 미래 증명 코드를 사용해야 구축되지 할 것입니다 openssl_encrypt openssl_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 모드를 사용합니다 . 암호화 공학의 충돌 과정을 거치지 않고 간단한 솔루션을 원한다면 직접 작성하지 말고 라이브러리를 사용하십시오.

보안 요구에 따라 다른 치퍼 방식도 사용할 수 있습니다. 사용 가능한 chipper 메소드를 찾으려면 openssl_get_cipher_methods 함수 를 참조하십시오 .


답변

하지 말아야 할 것

경고 :
이 답변은 ECB를 사용합니다 . ECB는 암호화 모드가 아니며 빌딩 블록 일뿐입니다. 이 답변에 설명 된대로 ECB를 사용해도 실제로 문자열을 안전하게 암호화하지는 않습니다. 코드에서 ECB를 사용하지 마십시오. 좋은 해결책 은 Scott의 답변 을 참조하십시오 .

나는 그것을 스스로 얻었다. 실제로 나는 구글에서 일부 답변을 발견하고 뭔가를 수정했습니다. 그러나 결과는 완전히 안전하지 않습니다.

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


답변

라 라벨 프레임 워크

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

참고 : config / app.php 파일의 키 옵션에서 16, 24 또는 32 자의 임의 문자열을 설정하십시오. 그렇지 않으면 암호화 된 값이 안전하지 않습니다.


답변

업데이트

PHP 7 준비 버전. PHP OpenSSL Library의 openssl_encrypt 함수를 사용합니다 .

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


답변

역사적 참고 사항 : 이것은 PHP4 당시 작성되었습니다. 이것이 우리가 지금 “레거시 코드”라고 부르는 것입니다.

나는 역사적인 대답을 위해이 답변을 남겼습니다. 그러나 일부 방법은 더 이상 사용되지 않으며 DES 암호화 방법은 권장되지 않습니다.

나는 두 가지 이유로이 코드를 업데이트하지 않았습니다 : 1) 더 이상 PHP에서 직접 암호화 방법으로 작업하지 않으며, 2)이 코드는 여전히 의도 된 목적을 제공합니다. 암호화가 작동하는 방법에 대한 최소의 간단한 개념 PHP에서.

사람들이 10-20 줄 이하의 코드로 시작할 수있는 유사하게 “모두를위한 PHP 암호화”종류의 소스를 찾으면 의견으로 알려주십시오.

그 외에도 초기 PHP4 미니멀리즘 암호화 답변의 클래식 에피소드를 즐기십시오.


이상적으로는 널리 사용되고 매우 유용한 다양한 작업으로 mcrypt PHP 라이브러리에 액세스하거나 액세스 할 수 있습니다. 다음은 여러 종류의 암호화와 예제 코드에 대한 요약입니다. 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) 여기에 나열된 모든 종류의 쉽게 구현할 수있는 암호화는 우발적이거나 의도적 인 누출의 경우 눈을 피하거나 노출을 제한하려는 아주 중요한 정보를 합리적으로 보호 할 수 있습니다. 그러나 키가 웹 서버에서 일반 텍스트로 저장되는 방식으로 볼 때 데이터를 얻을 수 있으면 해독 키를 얻을 수 있습니다.

그것이 재미있을 것입니다 🙂


답변

라이브러리를 사용하지 않으려면 (필수) 다음과 같은 것을 사용하십시오 (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);