programing

mcrypt는 권장되지 않습니다.대안은 무엇입니까?

copyandpastes 2022. 10. 2. 22:08
반응형

mcrypt는 권장되지 않습니다.대안은 무엇입니까?

mcrypt-extension은 권장되지 않습니다.여기에 게재된 코멘트에 따라 PHP 7.2에서 삭제됩니다.그래서 비밀번호를 암호화할 다른 방법을 찾고 있습니다.

저는 지금 이렇게 쓰고 있어요

mcrypt_encrypt(MCRYPT_RIJNDAEL_128, md5($key, true), $string, MCRYPT_MODE_CBC, $iv)

패스워드를 암호화하는 최선의/강력한 방법이 필요합니다.암호화된 패스워드는 물론 PHP 7.xx에서 지원되어야 하며, 고객이 새로운 패스워드를 생성하지 않고 '복구'하는 옵션을 원하기 때문에 복호화도 가능해야 합니다.

암호를 해독할 수 없도록 암호를 해시하는 것이 가장 좋습니다.이로 인해 데이터베이스 또는 파일에 대한 액세스 권한을 얻은 공격자가 문제를 약간 더 어렵게 만들 수 있습니다.

데이터를 암호화하여 복호화할 필요가 있는 경우, 암호화/암호화를 보호하기 위한 가이드는 https://paragonie.com/white-paper/2015-secure-php-data-encryption 에서 구할 수 있습니다.링크를 요약하면:

  • 립소듐 사용 - PHP 확장자
  • Libsodium을 사용할 수 없다면 디퓨즈/php-encryption 사용 - 스트레이트 PHP 코드
  • Libsodium 또는 defuse/php-encryption을 사용할 수 없는 경우 OpenSSL을 사용하십시오.많은 서버에 이미 설치되어 있습니다.그렇지 않은 경우 --with-sl[=]로 컴파일할 수 있습니다.DIR]

@rqLizard가 제안하는 바와 같이 / PHP 함수를 openssl_decrypt사용하면 Rijndael 암호화로도 알려진 AES(Advanced Encryption Standard)를 구현하는 데 더 나은 대안을 제공할 수 있습니다.

php.net에서 Scott의 코멘트는 다음과 같습니다.

2015년를 위한 2015년 데이터 암호화/암호화 코드를 .openssl_encrypt() ★★★★★★★★★★★★★★★★★」openssl_decrypt(). 기반이 되는 라이브러리(libmcrypt는 2007년OpenSSL( OpenSSL을 활용)보다 저하되었습니다.AES-NI최신 프로세서에 탑재되어 있으며 캐시 캐시 스위칭이 안전합니다).

ㅇㅇㅇㅇ.MCRYPT_RIJNDAEL_256AES-256리젠다엘네가 원한다면AES-256mcrypt , 을.MCRYPT_RIJNDAEL_12832살OpenSSL을 모드를 하게 알 수 OpenSSL).aes-128-cbc »aes-256-ctr를 참조해 주세요.

OpenSSL은 또한 mcrypt의 NULL 바이트 패딩 대신 CBC 모드를 사용한 PKCS7 패딩을 사용합니다.따라서 mcrypt는 OpenSSL보다 코드를 패딩 오라클 공격에 취약하게 만들 가능성이 높습니다.

마지막으로 암호문(암호화 후 MAC)을 인증하지 않으면 잘못된 것입니다.

추가 정보:

코드 예시

예 #1

PHP 7.1+에 대한 GCM 모드의 AES 인증 암호화 예시

<?php
//$key should have been previously generated in a cryptographically safe way, like openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$cipher = "aes-128-gcm";
if (in_array($cipher, openssl_get_cipher_methods()))
{
    $ivlen = openssl_cipher_iv_length($cipher);
    $iv = openssl_random_pseudo_bytes($ivlen);
    $ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
    //store $cipher, $iv, and $tag for decryption later
    $original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options=0, $iv, $tag);
    echo $original_plaintext."\n";
}
?>

예 #2

PHP 5.6+용 AES 인증 암호화 예시

<?php
//$key previously generated safely, ie: openssl_random_pseudo_bytes
$plaintext = "message to be encrypted";
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
$ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );

//decrypt later....
$c = base64_decode($ciphertext);
$ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC");
$iv = substr($c, 0, $ivlen);
$hmac = substr($c, $ivlen, $sha2len=32);
$ciphertext_raw = substr($c, $ivlen+$sha2len);
$original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options=OPENSSL_RAW_DATA, $iv);
$calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary=true);
if (hash_equals($hmac, $calcmac))//PHP 5.6+ timing attack safe comparison
{
    echo $original_plaintext."\n";
}
?>

예 3

위의 예에 따라 사용자의 세션 ID를 암호화하기 위한 다음 코드를 변경했습니다.

class Session {

  /**
   * Encrypts the session ID and returns it as a base 64 encoded string.
   *
   * @param $session_id
   * @return string
   */
  public function encrypt($session_id) {
    // Get the MD5 hash salt as a key.
    $key = $this->_getSalt();
    // For an easy iv, MD5 the salt again.
    $iv = $this->_getIv();
    // Encrypt the session ID.
    $encrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $session_id, MCRYPT_MODE_CBC, $iv);
    // Base 64 encode the encrypted session ID.
    $encryptedSessionId = base64_encode($encrypt);
    // Return it.
    return $encryptedSessionId;
  }

  /**
   * Decrypts a base 64 encoded encrypted session ID back to its original form.
   *
   * @param $encryptedSessionId
   * @return string
   */
  public function decrypt($encryptedSessionId) {
    // Get the MD5 hash salt as a key.
    $key = $this->_getSalt();
    // For an easy iv, MD5 the salt again.
    $iv = $this->_getIv();
    // Decode the encrypted session ID from base 64.
    $decoded = base64_decode($encryptedSessionId);
    // Decrypt the string.
    $decryptedSessionId = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $decoded, MCRYPT_MODE_CBC, $iv);
    // Trim the whitespace from the end.
    $session_id = rtrim($decryptedSessionId, "\0");
    // Return it.
    return $session_id;
  }

  public function _getIv() {
    return md5($this->_getSalt());
  }

  public function _getSalt() {
    return md5($this->drupal->drupalGetHashSalt());
  }

}

다음과 같이 입력합니다.

class Session {

  const SESS_CIPHER = 'aes-128-cbc';

  /**
   * Encrypts the session ID and returns it as a base 64 encoded string.
   *
   * @param $session_id
   * @return string
   */
  public function encrypt($session_id) {
    // Get the MD5 hash salt as a key.
    $key = $this->_getSalt();
    // For an easy iv, MD5 the salt again.
    $iv = $this->_getIv();
    // Encrypt the session ID.
    $ciphertext = openssl_encrypt($session_id, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
    // Base 64 encode the encrypted session ID.
    $encryptedSessionId = base64_encode($ciphertext);
    // Return it.
    return $encryptedSessionId;
  }

  /**
   * Decrypts a base 64 encoded encrypted session ID back to its original form.
   *
   * @param $encryptedSessionId
   * @return string
   */
  public function decrypt($encryptedSessionId) {
    // Get the Drupal hash salt as a key.
    $key = $this->_getSalt();
    // Get the iv.
    $iv = $this->_getIv();
    // Decode the encrypted session ID from base 64.
    $decoded = base64_decode($encryptedSessionId, TRUE);
    // Decrypt the string.
    $decryptedSessionId = openssl_decrypt($decoded, self::SESS_CIPHER, $key, $options=OPENSSL_RAW_DATA, $iv);
    // Trim the whitespace from the end.
    $session_id = rtrim($decryptedSessionId, '\0');
    // Return it.
    return $session_id;
  }

  public function _getIv() {
    $ivlen = openssl_cipher_iv_length(self::SESS_CIPHER);
    return substr(md5($this->_getSalt()), 0, $ivlen);
  }

  public function _getSalt() {
    return $this->drupal->drupalGetHashSalt();
  }

}

명확히 하자면, 위의 변경은 진정한 변환이 아닙니다.두 가지 암호화에서는 다른 블록 크기와 다른 암호화된 데이터가 사용되기 때문입니다. 패딩은 패딩은 디폴트 패딩이 다릅니다.MCRYPT_RIJNDAEL는 비표준 늘패딩만 지원합니다.@zaph


기타 주의사항(@zaph 댓글 참조):

  • Rijndael 128 (MCRYPT_RIJNDAEL_128)는 AES와 동등하지만 Rijndael 256(단,MCRYPT_RIJNDAEL_256 )는 256은 256비트의 블록사이즈를 나타내므로 AES-256이 아닙니다만, AES에는 128비트라는1개의 블록사이즈밖에 없습니다.즉, 기본적으로 블록사이즈가 256비트인 Rijndael(MCRYPT_RIJNDAEL_256mcrypt 개발자의 선택으로 인해 이름이 잘못 지정되었습니다.@zaph
  • 블록 사이즈가 256인 Rijndael은 128비트의 블록사이즈보다 안전성이 떨어집니다.이는 128비트가 리뷰와 사용이 훨씬 많기 때문입니다.둘째, AES는 일반적으로 사용할 수 있지만 256비트 블록사이즈의 Rijndael은 사용할 수 없다는 점에서 상호운용성이 저해됩니다.
  • Rijndael의 블록 사이즈가 다른 암호화에서는 다른 암호화된 데이터가 생성됩니다.

    를 들어, 「」라고 하는 것은,MCRYPT_RIJNDAEL_256하지 않다)AES-256된 키에키서 Rijndael은 256비트입니다.aes-256-cbc【리젠다엘】128년, 【256년】따라서 mcrypt가 이 숫자를 사용하여 블록 크기를 지정하고 OpenSSL은 이 숫자를 사용하여 키 크기를 지정하므로 완전히 다른 암호화된 데이터를 생성하는 블록 크기를 사용합니다(AES는 128비트 블록 크기가 1개뿐).따라서 기본적으로 AES는 Rijndael이며 블록 크기는 128비트, 키 크기는 128비트, 192비트 및 256비트입니다.OpenSSL의 Rijndael 128의 AES입니다.

다른 답변에서도 알 수 있듯이 OpenSSL을 사용하는 것이 가장 좋은 해결책입니다.PHP에 내장되어 있어 외부 라이브러리가 필요 없습니다.다음은 간단한 예입니다.

암호화 방법:

function encrypt($key, $payload) {
  $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  $encrypted = openssl_encrypt($payload, 'aes-256-cbc', $key, 0, $iv);
  return base64_encode($encrypted . '::' . $iv);
}

복호화 방법:

function decrypt($key, $garble) {
    list($encrypted_data, $iv) = explode('::', base64_decode($garble), 2);
    return openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv);
}

레퍼런스 링크: https://www.shift8web.ca/2017/04/how-to-encrypt-and-execute-your-php-code-with-mcrypt/

Rijndael의 순수 PHP 구현은 작곡가 패키지로 사용 가능한 phpseclib와 함께 존재하며 PHP 7.3에서 작동합니다(제가 테스트했습니다).

phpseclib 문서에는 기본 변수(암호화, 모드, 키 크기, 비트 크기)를 입력한 후 샘플 코드를 생성하는 페이지가 있습니다.Rijndael, ECB, 256, 256에 대해 다음을 출력합니다.

내 암호로 된 암호

$decoded = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, ENCRYPT_KEY, $term, MCRYPT_MODE_ECB);

도서관에서는 이렇게 작업합니다.

$rijndael = new \phpseclib\Crypt\Rijndael(\phpseclib\Crypt\Rijndael::MODE_ECB);
$rijndael->setKey(ENCRYPT_KEY);
$rijndael->setKeyLength(256);
$rijndael->disablePadding();
$rijndael->setBlockLength(256);

$decoded = $rijndael->decrypt($term);

*$termbase64_decoded

phpseclib pollyfill 패키지를 사용할 수 있습니다.rijndael 256을 사용한 암호화/복호화에는 open ssl 또는 libsodium을 사용할 수 없습니다.또 다른 문제는 코드를 교체할 필요가 없다는 것입니다.

OpenSSL over OpenSSL을 .mcrypt활발하게 개발되고 유지되고 있기 때문입니다.보안, 유지보수성 및 휴대성이 향상됩니다.둘째, AES 암호화/복호화를 훨씬 빠르게 수행합니다.하지만 PKCS7 패딩을 지정할 수 .OPENSSL_ZERO_PADDING요하필키와 하려면 , 32 바이트의 바이너리 키를 지정할 수 있습니다.aes-256-cbc 더 명백함MCRYPT_RIJNDAEL_128.

다음은 Mcrypt를 사용한 코드 예시입니다.

인증되지 않은 AES-256-CBC 암호화 라이브러리는 PKCS7 패딩으로 Mcrypt로 작성되었습니다.

/**
 * This library is unsafe because it does not MAC after encrypting
 */
class UnsafeMcryptAES
{
    const CIPHER = MCRYPT_RIJNDAEL_128;

    public static function encrypt($message, $key)
    {
        if (mb_strlen($key, '8bit') !== 32) {
            throw new Exception("Needs a 256-bit key!");
        }
        $ivsize = mcrypt_get_iv_size(self::CIPHER);
        $iv = mcrypt_create_iv($ivsize, MCRYPT_DEV_URANDOM);

        // Add PKCS7 Padding
        $block = mcrypt_get_block_size(self::CIPHER);
        $pad = $block - (mb_strlen($message, '8bit') % $block, '8bit');
        $message .= str_repeat(chr($pad), $pad);

        $ciphertext = mcrypt_encrypt(
            MCRYPT_RIJNDAEL_128,
            $key,
            $message,
            MCRYPT_MODE_CBC,
            $iv
        );

        return $iv . $ciphertext;
    }

    public static function decrypt($message, $key)
    {
        if (mb_strlen($key, '8bit') !== 32) {
            throw new Exception("Needs a 256-bit key!");
        }
        $ivsize = mcrypt_get_iv_size(self::CIPHER);
        $iv = mb_substr($message, 0, $ivsize, '8bit');
        $ciphertext = mb_substr($message, $ivsize, null, '8bit');

        $plaintext = mcrypt_decrypt(
            MCRYPT_RIJNDAEL_128,
            $key,
            $ciphertext,
            MCRYPT_MODE_CBC,
            $iv
        );

        $len = mb_strlen($plaintext, '8bit');
        $pad = ord($plaintext[$len - 1]);
        if ($pad <= 0 || $pad > $block) {
            // Padding error!
            return false;
        }
        return mb_substr($plaintext, 0, $len - $pad, '8bit');
    }
}

다음은 Open을 사용하여 작성된 버전입니다.SSL:

/**
 * This library is unsafe because it does not MAC after encrypting
 */
class UnsafeOpensslAES
{
    const METHOD = 'aes-256-cbc';

    public static function encrypt($message, $key)
    {
        if (mb_strlen($key, '8bit') !== 32) {
            throw new Exception("Needs a 256-bit key!");
        }
        $ivsize = openssl_cipher_iv_length(self::METHOD);
        $iv = openssl_random_pseudo_bytes($ivsize);

        $ciphertext = openssl_encrypt(
            $message,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $iv
        );

        return $iv . $ciphertext;
    }

    public static function decrypt($message, $key)
    {
        if (mb_strlen($key, '8bit') !== 32) {
            throw new Exception("Needs a 256-bit key!");
        }
        $ivsize = openssl_cipher_iv_length(self::METHOD);
        $iv = mb_substr($message, 0, $ivsize, '8bit');
        $ciphertext = mb_substr($message, $ivsize, null, '8bit');

        return openssl_decrypt(
            $ciphertext,
            self::METHOD,
            $key,
            OPENSSL_RAW_DATA,
            $iv
        );
    }
}

출처: PHP 코드에 MCRYPT라는 단어를 입력하는 것은 잘못된 것입니다.

저는 이것을 PHP 7.2.x에서 사용하고 있습니다만, 저는 정상적으로 동작하고 있습니다.

public function make_hash($userStr){
        try{
            /** 
             * Used and tested on PHP 7.2x, Salt has been removed manually, it is now added by PHP 
             */
             return password_hash($userStr, PASSWORD_BCRYPT);
            }catch(Exception $exc){
                $this->tempVar = $exc->getMessage();
                return false;
            }
        }

다음 함수를 사용하여 해시를 인증합니다.

public function varify_user($userStr,$hash){
        try{
            if (password_verify($userStr, $hash)) {
                 return true;
                }
            else {
                return false;
                }
            }catch(Exception $exc){
                $this->tempVar = $exc->getMessage();
                return false;
            }
        }

예:

  //create hash from user string

 $user_password = $obj->make_hash2($user_key);

이 해시를 인증하려면 다음 코드를 사용합니다.

if($obj->varify_user($key, $user_key)){
      //this is correct, you can proceed with  
    }

그게 다예요.

지적했듯이 사용자의 비밀번호를 복호화 가능한 형식으로 저장해서는 안 됩니다.리버서블 암호화는 해커가 사용자의 비밀번호를 쉽게 알아낼 수 있도록 합니다.이는 사용자의 계정이 다른 사이트에서 동일한 비밀번호를 사용할 경우 사용자의 계정을 위험에 빠뜨리는 것으로 확장됩니다.

는 랜덤 해시를 위한 합니다. - PHP는 단방향 해시 암호화입니다.password_hash() ★★★★★★★★★★★★★★★★★」password_verify()해시는 자동으로 랜덤으로 판매되기 때문에 해커가 패스워드를 리버스 엔지니어링하기 위해 미리 컴파일된 패스워드 해시 테이블을 이용할 수 있는 방법은 없습니다.를 합니다.PASSWORD_DEFAULT옵션과 향후 버전의 PHP는 코드를 업데이트하지 않고도 패스워드 해시를 생성하기 위해 보다 강력한 알고리즘을 자동으로 사용합니다.

Crypto 오브젝트를 번역할 수 있었습니다.

  • 이전 데이터를 해독하려면 mcrypt를 사용하여 php 복사본을 가져옵니다.http://php.net/get/php-7.1.12.tar.gz/from/a/mirror,에 접속하여 컴파일한 후 ext/mcrypt 확장자(configure;make;make install)를 추가했습니다.php.ini에도 exponstion=mcrypt.so 행을 추가해야 할 것 같습니다.모든 데이터를 암호화하지 않고 중간 버전의 데이터를 구축하는 일련의 스크립트입니다.

  • openssl용 공용 키 및 개인 키 구축

    openssl genrsa -des3 -out pkey.pem 2048
    (set a password)
    openssl rsa -in pkey.pem -out pkey-pub.pem -outform PEM -pubout
    
  • (공개 키를 사용하여) 암호화하려면 openssl_seal을 사용합니다.제가 읽은 바로는 RSA 키를 사용하는 openssl_module은 키 길이보다 11바이트 적게 제한됩니다(http://php.net/manual/en/function.openssl-public-encrypt.php의 Thomas Horsten 코멘트 참조).

    $pubKey = openssl_get_publickey(file_get_contents('./pkey-pub.pem'));
    openssl_seal($pwd, $sealed, $ekeys, [ $pubKey ]);
    $encryptedPassword = base64_encode($sealed);
    $key = base64_encode($ekeys[0]);
    

원시 이진법을 저장할 수 있습니다.

  • 복호화 방법(개인 키를 사용)

    $passphrase="passphrase here";
    $privKey = openssl_get_privatekey(file_get_contents('./pkey.pem'), $passphrase);
    // I base64_decode() from my db columns
    openssl_open($encryptedPassword, $plain, $key, $privKey);
    echo "<h3>Password=$plain</h3>";
    

추신: 빈 문자열("")을 암호화할 수 없습니다.

P.P.S. 이것은 비밀번호 데이터베이스용이지 사용자 확인을 위한 것이 아닙니다.

기능을 사용해야 합니다.

언급URL : https://stackoverflow.com/questions/41272257/mcrypt-is-deprecated-what-is-the-alternative

반응형