【问题标题】:Detect wrong decryption key while working with mcrypt functions in PHP在 PHP 中使用 mcrypt 函数时检测错误的解密密钥
【发布时间】:2015-03-08 12:49:38
【问题描述】:

我编写了以下代码来制作加密和解密机制。一切正常,但如果有人在解密时输入了错误的密钥,我想防止显示不可读的字符。相反,我想向他显示某个错误消息。我该怎么做?
我的 PHP 代码:

<?php

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

function encrypt($plaintext,$key) {
    global $iv, $iv_size;
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
    $ciphertext = $iv . $ciphertext;
    return base64_encode($ciphertext);
}

function decrypt($ciphertext_base64,$key) {
    global $iv, $iv_size;
    $ciphertext_dec = base64_decode($ciphertext_base64);
    $iv_dec = substr($ciphertext_dec, 0, $iv_size);
    $ciphertext_dec = substr($ciphertext_dec, $iv_size);
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
}

echo decrypt(encrypt('Hello World','123'),'123');
// Correct key used for decryption and result will be `Hello world`

echo decrypt(encrypt('Hello World','123'),'321');
// Wrong key used for decryption and result will be something like :ŘI¨ĄěđŘcSNŔ¶¸˘ÚE‘Z‰ŃZŃ9

?>

【问题讨论】:

  • 在现实生活中,你怎么知道解密的钥匙给错了??除非你已经知道解密的正确答案??如果您或某人保留未加密的数据以用于比较以检查解密,那么哪种方式会破坏加密任何内容。
  • @RiggsFolly,我正在编写一个为人们加密和解密数据的应用程序。如果他们只输入正确的密钥,他们就可以看到原始数据。否则,我必须要求他们再次输入密钥。要知道我的意思,请使用 onetimesecret.com 并使用密钥创建一次性链接。然后尝试使用错误的键打开该链接,看看会发生什么。
  • 是的,但我的意思是,知道密钥错误的唯一方法是知道密钥是什么。如果我使用你的系统来创建我的秘密,我不得不希望你不要保留密钥。 否则它不是秘密所以我绝对不会使用你的系统或onetimesecret.com
  • @RiggsFolly,是的,你是对的。实际上我不知道 onetimesecret.com 中使用了什么机制。但是,正如您所说,我必须避免存储任何可以由我自己或其他人轻松返回的原始数据。
  • @RiggsFolly,你对汉字所说的话有何看法? (方法一和二)

标签: php encryption key mcrypt


【解决方案1】:

只有在您对纯文本有所了解的情况下,才能检测密钥是否正确。

(Bad) 方法一:在纯文本中添加静态字符串

您可以修改您的 encrypt() 函数,以便它始终在纯文本中添加一些内容,例如$plaintext = 'correct key' . $plaintext;.

在您的 decrypt() 函数中,您现在可以检查此字符串是否存在并在返回之前将其删除。

您必须将decrypt() 的最后一行替换为:

$decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
if (strpos($decrypted_text, 'correct key') === 0) {
    return substr($decrypted_text, strlen('correct key'));
} else {
    // key was wrong
    return false;
}

这里的问题是知道部分加密消息可能会削弱加密的安全性。所以不要那样做。

(更好)方法2:用哈希验证纯文本

您可以只保存它的指纹(例如 SHA1 哈希),而不是修改纯文本。

像这样修改encrypt()函数:

return sha1($plaintext) . base64_encode($ciphertext);

decrypt() 函数是这样的:

function decrypt($ciphertext, $key) {
    global $iv, $iv_size;

    $fingerprint = substr($ciphertext, 0, 32);
    $ciphertext_base64 = substr($ciphertext, 32);

    $ciphertext_dec = base64_decode($ciphertext_base64);
    $iv_dec = substr($ciphertext_dec, 0, $iv_size);
    $ciphertext_dec = substr($ciphertext_dec, $iv_size);

    $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
    if (sha1($decrypted_text) == $fingerprint) {
        return $decrypted_text;
    } else {
        // key was wrong
        return false;
    }
}

【讨论】:

  • 最好把hash放到明文里一起加密。这样做不会泄露任何有关明文的信息。当然,更好的选择是使用 Encrypt-then-MAC,但更难做到正确。参考:crypto.stackexchange.com/questions/202/…
  • 我知道这是一个非常古老的例子,但这是我在寻找在我发现的一些代码中验证相同事物的方法时出现的第一个例子。这在 2015 年是个坏主意,在 2020 年更糟。请不要这样做。在加密文本旁边对纯文本进行 SHA1 基本上只是对 SHA 彩虹表的邀请。这基本上只是通过默默无闻的安全性,最坏的情况是一个相当大的安全漏洞。老实说,糟糕的方法更安全。
猜你喜欢
  • 2015-11-10
  • 2019-04-13
  • 2017-01-03
  • 2011-08-16
  • 2013-12-28
  • 2012-04-11
  • 2023-03-03
  • 2012-07-27
  • 2016-01-26
相关资源
最近更新 更多