web-dev-qa-db-ja.com

mcryptは非推奨です、代替手段は何ですか?

Mcrypt-extensionは 廃止予定 は投稿されたコメントに従ってPHP 7.2で削除されます こちら だから私はパスワードを暗号化するための代替方法を探しています。

今私は以下のようなものを使っています

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

パスワードを暗号化するための最善/最強の方法についてのあなたの意見が必要です。暗号化されたパスワードはもちろんPHP 7.xxによってサポートされ、私の顧客は「回復する」オプションを望んでいるので解読可能であるべきです新しいパスワードを生成せずにパスワードを変更します。

78
Piet

パスワードを復号化できないようにパスワードをハッシュすることをお勧めします。これにより、データベースやファイルにアクセスした可能性がある攻撃者にとっては、状況がやや困難になります。

データを暗号化して復号化できるようにする必要がある場合は、安全な暗号化/復号化のガイドを https://paragonie.com/white-paper/2015-secure-php-data-から入手できます。暗号化 。そのリンクをまとめると:

  • Libsodium - A PHP拡張子を使用
  • Libsodiumを使用できない場合は、 defuse/php-encryption - Straight PHP codeを使用してください。
  • もしあなたがLibsodiumを使うことができないか/ php暗号化を無効にするなら、 OpenSSL - を使ってください。そうでなければ、--with-openssl [= DIR]でコンパイルできます。
38
Phil

@rqLizard で示唆されているように、代わりに openssl_encrypt / openssl_decrypt PHP関数を使うことができます。これは AES (高度な暗号化標準)Rijndael暗号化とも呼ばれます。

次の通り Scottのphp.netへのコメント

2015年にデータを暗号化/暗号化するコードを書いているのなら、openssl_encrypt()openssl_decrypt()を使うべきです。基礎となるライブラリ(libmcryptname__)は2007年以来放棄されており、OpenSSL(最近のプロセッサではAES-NIを利用し、キャッシュタイミングに対して安全です)よりもはるかに性能が劣ります。

また、MCRYPT_RIJNDAEL_256AES-256ではありません。これはRijndaelブロック暗号の別の変種です。 mcryptname__にAES-256が必要な場合は、32バイトのキーでMCRYPT_RIJNDAEL_128を使用する必要があります。 OpenSSLでは、どのモードを使用しているかがより明確になります(つまり、aes-128-cbcaes-256-ctr)。

OpenSSLは、mcryptのNULLバイトパディングではなく、CBCモードでPKCS7パディングも使用します。したがって、mcryptを使用すると、OpenSSLよりもコードがOracleのパディング攻撃に対して脆弱になる可能性が高くなります。

最後に、あなたがあなたの暗号文を認証していなければ(Encrypt Then 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();
  }

}

明確にするために、2つの暗号化は異なるブロックサイズと異なる暗号化データを使用するため、上記の変更は正しい変換ではありません。さらに、デフォルトのパディングは異なり、MCRYPT_RIJNDAELは非標準のnullパディングのみをサポートします。 @zaph


追加のメモ(@ zaphのコメントから):

  • Rijndael 128MCRYPT_RIJNDAEL_128isAES に相当しますが、 Rijndael 256MCRYPT_RIJNDAEL_256isではないAES-256 256は256ビットのブロックサイズを指定しますが、 AES は1つのブロックサイズ、128ビットのみを持ちます。したがって、基本的に256ビットのブロックサイズを持つRijndael(MCRYPT_RIJNDAEL_256)は、 mcrypt developers)による選択のために誤って命名されています。 @zaph
  • ブロックサイズが256のRijndaelは、128ビットのブロックサイズよりも安全性が劣る可能性があります。これは、後者の方がはるかに多くのレビューと用途を持っているためです。第二に、AESが一般的に利用可能である一方で、256ビットのブロックサイズを持つRijndaelがそうではないという点で、相互運用性は妨げられます。
  • Rijndaelのブロックサイズを変えて暗号化すると、異なる暗号化データが生成されます。

    たとえば、MCRYPT_RIJNDAEL_256AES-256と同等ではありません)は、256ビットのサイズと渡されたキーに基づくキーサイズを持つRijndaelブロック暗号の異なる変形を定義します。ここで、aes-256-cbcはブロックサイズが128ビットのRijndaelです。 256ビットのキーサイズ。そのため、mcryptがブロックサイズを指定するのに数値を使用するのとは異なるブロックサイズを使用して、まったく異なる暗号化データを生成します(OpenSSLはキーサイズを指定するのに数値を使用します)。したがって、基本的にAESはRijndaelで、ブロックサイズは128ビット、キーサイズは128、192、および256ビットです。したがって、OpenSSLではRijndael 128と呼ばれるAESを使用することをお勧めします。

21
kenorb

Rijndaelの純粋なPHPによる実装は phpseclib が作曲者パッケージとして利用可能で、PHP 7.3(私がテスト済み)で動作します。

Phpseclibドキュメントには、基本変数(暗号、モード、鍵サイズ、ビットサイズ)を入力した後に サンプルコード が生成されるページがあります。 Rijndael、ECB、256、256に対して次のように出力されます。

mycryptを使ったコード

$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でした

6
Pentium10

あなたは phpseclib pollyfillパッケージを使うことができます。あなたは暗号化/暗号解読のためにopen sslやlibsodiumをrijndael 256で使うことはできません。

4

OpenSSLは積極的に開発され維持されているので、mcryptよりOpenSSLを使用するべきです。それはより良いセキュリティ、保守容易性および移植性を提供します。第二に、それははるかに速くAES暗号化/復号化を実行します。デフォルトではPKCS7パディングを使用しますが、必要に応じてOPENSSL_ZERO_PADDINGを指定できます。 32バイトのバイナリキーで使用するには、aes-256-cbcを指定することができます。これはMCRYPT_RIJNDAEL_128よりも明らかです。

これがMcryptを使ったコード例です。

PKCS7パディングを使用してMcryptで書かれた未認証のAES-256-CBC暗号化ライブラリ。

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

そしてこれがOpenSSLを使って書かれたバージョンです:

/**
 * 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という単語をタイプしているなら、間違っています

3
kenorb

指摘したように、あなたは解読可能なフォーマットであなたのユーザーのパスワードを保存するべきではありません。可逆暗号化はハッカーがあなたのユーザーのパスワードを見つけるための簡単な方法を提供します。

PHPは、ランダム塩漬けの一方向ハッシュ暗号化のための一対の強力な関数password_hash()password_verify()を提供します。ハッシュは自動的にランダムにソルト化されるので、ハッカーがパスワードをリバースエンジニアリングするためにパスワードハッシュのプリコンパイルされたテーブルを利用する方法はありません。 PASSWORD_DEFAULTオプションを設定すると、PHPの将来のバージョンでは、コードを更新しなくても自動的に強力なアルゴリズムを使用してパスワードハッシュが生成されます。

1

あなたは openssl_encrypt() 関数を使うべきです。

1
rqLizard

ここで他の答えで詳述されているように、私が見つけた最良の解決策は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/

1

Cryptoオブジェクトを翻訳できました

  • 古いデータを復号化するためにmcryptでphpのコピーを入手してください。 http://php.net/get/php-7.1.12.tar.gz/from/a/mirror に行き、コンパイルして、それからext/mcrypt拡張子(configure; make; make install)を追加しました。 php.iniにもextenstion = 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_encryptは、キー長より11バイト短い値に制限されています。 http://php.net/manual/en/function.openssl-public Thomas Horstenによる-encrypt.php コメント)

    $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.S空の文字列( "")は暗号化できません

P.P.S.これはパスワードデータベース用であり、ユーザー検証用ではありません。

0