7

我对这个主题进行了一系列研究,但遗憾的是我找不到在 PHP 中加密和解密文件的完美方法。这意味着我要做的是找到某种方法来加密和解密我的项目,而不用担心破解者知道我的算法。如果某些需要隐藏和隐藏的算法,一旦逻辑通过任何地方共享,它就无法解决我的问题,或者他们闯入我的服务器并获取源文件,那么它应该是使用相同解密的某种方式来解密它算法。以前我在 StackOverFlow 网站上找到了几篇很棒的帖子,但它仍然无法回答我的问题。

加密世界密码的最佳方式,从我通过阅读得出的结论。河豚加密。这是一种1000次迭代的哈希算法,使用相同规格的GPU,破解者需要7年才能解密。

显然,这使得在单向散列时无法解密。

  1. 如何在 PHP 中使用 bcrypt 对密码进行哈希处理?
  2. 为什么盐使字典攻击“不可能”?

在 PHP 中加密和解密密码的最佳方法,正如这个问题所引用的那样。参考我在网上查到的,sha1和md5都是crack&broken算法,即使我们把算法从

$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key))));

$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, sha1(md5($key)), $string, MCRYPT_MODE_CBC, sha1(md5(md5($key)))));

不是只是增加了解密的难度,但在时间问题上仍然可以破解吗?

  1. 使用 PHP 加密和解密密码的最佳方法?

我正在考虑使用我们的服务器处理器/硬盘 GUID 来生成盐和加密密码。

当黑客获得对服务器的访问权限并且他们可以使用 PHP 回显 GUID 并进行解密时,这仍然是一些愚蠢的方法。或者如果它有效,几年后我的网站就会遇到麻烦。原因是硬盘,处理器永远不会永远存在。当我的处理器或硬盘出现故障时,我的网站就会出现故障并丢失所有凭据。

更新

发现了这个问题,它在 PHP 中使用河豚进行解密。它是否解决了寻找安全的加密方式和其他人难以解密的问题?

  1. 如何在 php 中使用 Blowfish 算法解密?

谁能建议我应该如何克服这个问题?谢谢。

4

6 回答 6

5

请记住,为了破解密码,黑客首先必须能够访问加密的密码。为了做到这一点,他们将不得不损害服务器的安全性,如果站点编码正确(正确的转义或准备好的语句),这应该是不可能的。

最强大但最简单的加密形式之一是 XOR,但它完全取决于密钥。如果密钥与编码文本的长度相同,那么没有该密钥就完全牢不可破。即使密钥只有文本长度的一半,也极不可能被破坏。

不过,最后,无论您选择什么方法,都由您的 FTP/SSH/任何允许您访问服务器文件的密码保护。如果您自己的密码被泄露,黑客可以看到一切。

于 2012-08-20T04:22:04.570 回答
5

查看这篇有据可查的文章A reversible password encryption routine for PHP,适用于那些想要一个可逆密码加密例程的 PHP 开发人员。

即使此类用于密码加密,您也可以将其用于任何文本的加密/解密。

function encryption_class() {
    $this->errors = array();

    // Each of these two strings must contain the same characters, but in a different order.
    // Use only printable characters from the ASCII table.
    // Do not use single quote, double quote or backslash as these have special meanings in PHP.
    // Each character can only appear once in each string.
    $this->scramble1 = '! #$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~';
    $this->scramble2 = 'f^jAE]okIOzU[2&q1{3`h5w_794p@6s8?BgP>dFV=m D<TcS%Ze|r:lGK/uCy.Jx)HiQ!#$~(;Lt-R}Ma,NvW+Ynb*0X';

    if (strlen($this->scramble1) <> strlen($this->scramble2)) {
        trigger_error('** SCRAMBLE1 is not same length as SCRAMBLE2 **', E_USER_ERROR);
    } // if

    $this->adj = 1.75;  // this value is added to the rolling fudgefactors
    $this->mod = 3;     // if divisible by this the adjustment is made negative
}

注意

如果您使用 PHP 版本 >= 5.3.3,则必须将类名从更改encryption_class__construct

原因:

As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor.

用法:

$crypt = new encryption_class();

$crypt->setAdjustment(1.75); // 1st adjustment value (optional)
$crypt->setModulus(3); // 2nd adjustment value (optional)

/**
 * 
 * @param string $key - Your encryption key
 * @param string $sourceText - The source text to be encrypted
 * @param integer $encLen - positive integer indicating the minimum length of encrypted text
 * @return string - encrypted text
 */
$encrypt_result = $crypt->encrypt($key, $sourceText, $encLen);

/**
 * 
 * @param string $key - Your encryption key (same used for encryption)
 * @param string $encrypt_result - The text to be decrypted
 * @return string - decrypted text
 */
$decrypt_result = $crypt->decrypt($key, $encrypt_result);

更新:

上面的类不是用来加密文件的,但是你可以!!!

  1. base64_encode您的源文本(文件内容)
  2. 对于实际加密,将上述 enc/dec 类应用于 base64 编码文本
  3. 对于解密,将上面的 enc/dec 类应用于实际加密的文本
  4. base64_decode将为您提供实际的文件内容(您可以使用此内容保存文件副本)

我已经加密了一张图片,解密回来并保存到一个新文件中!!!签出代码。

//class for encrypt/decrypt routines 
require 'class.encryption.php';

//configuring your security levels
$key = 'This is my secret key; with symbols (@$^*&<?>/!#_+), cool eh?!!! :)';
$adjustment = 1.75;
$modulus = 2;

//customizing
$sourceFileName = 'source-image.png';
$destFileName = 'dest-image.png';
$minSpecifiedLength = 512;

//base64 encoding file contents, to get all characters in our range
//binary too!!!
$sourceText = base64_encode(file_get_contents($sourceFileName));

$crypt = new encryption_class();
$crypt->setAdjustment($adjustment); //optional
$crypt->setModulus($modulus); //optional

//encrypted text
$encrypt_result = $crypt->encrypt($key, $sourceText, $minSpecifiedLength);

//receive initial file contents after decryption
$decrypt_result = base64_decode($crypt->decrypt($key, $encrypt_result));

//save as new file!!!
file_put_contents($destFileName, $decrypt_result);
于 2012-08-24T07:06:39.553 回答
5

你的问题导致两个不同的答案。这是一个重要的区别,您是否需要稍后解密数据(如文件),或者您是否可以使用单向哈希(用于密码)。

单向哈希

如果您不需要解密数据(密码),则应使用散列函数。这更安全,因为即使攻击者控制了您的服务器和数据库,他也不应该能够检索到原始密码。由于用户经常在多个网站上使用他们的密码,因此至少他也无法访问其他网站。

正如您已经说过的,当今最推荐的哈希函数之一是bcrypt。尽管它起源于河豚算法,但它实际上是一个散列函数(不是加密)。Bcrypt 是专门为散列密码而设计的,因此速度很慢(需要计算时间)。建议使用完善的库,如phpass,如果您想了解如何实现它,可以阅读这篇文章,我试图解释最重要的点。

加密

如果您需要稍后解密您的数据(文件),您无法阻止控制您服务器的攻击者也可以解密文件(毕竟服务器必须能够解密它)。所有这些加起来就是在哪里存储密钥的问题。你唯一能做的就是让获取钥匙变得更加困难。

这意味着,如果您将密钥存储在文件中,它应该位于http 根目录之外,因此无论如何都不能从 Internet 访问它。您可以将其存储在不同的服务器上,因此攻击者需要控制两台服务器,但随后您将面临服务器之间安全通信的问题。在任何情况下,您都可以使盗窃更加困难,但您无法完全防止盗窃。

根据您的情况,您可以在计算机上本地加密文件,并且仅将加密文件存储在服务器上。服务器将无法自行解密文件,因此它们是安全的。

于 2012-08-25T09:16:57.367 回答
2

所以你已经知道了加盐和散列,但你也可以“拉伸”你的密码,而不是只对每个密码进行一次散列,而是对它进行数千次散列。这将减慢蛮力攻击并增加散列算法的寿命。有趣的是,它通过故意减慢您的服务器来工作......

我建议您编写自己的自定义哈希函数。首先,您在密码中添加盐,然后选择一个哈希算法(例如 sha512,或者可能是一种旨在为此目的效率低下的新算法)并将其哈希 10,000 次,然后将其存储在数据库中. 正如您已经知道的那样,当用户登录时,您只需通过相同的算法运行他们的输入并查看它是否匹配,而不是反转哈希值。

编写自己的哈希函数的美妙之处在于,当需要更新哈希算法时,因为旧的哈希算法容易受到暴力攻击,您所要做的就是添加到您的哈希函数中,获取旧哈希的结果算法,重新加盐,并使用您的新算法再次对其进行哈希处理。您可以使用当时认为安全的任何哈希算法。然后,您可以简单地使用散列函数的新部分重新散列已存储在数据库中的每个密码,从而确保向后兼容性。根据您拥有的用户数量和服务器的速度,执行此更新可能只需要几秒钟。

但是,仍然存在一个漏洞。如果黑客拥有您数据库的旧副本并破解它,他仍然知道任何尚未更改密码的用户的密码。解决此问题的唯一方法是要求您的用户偶尔更改他们的密码,这可能适合您的网站,也可能不适合您的网站,具体取决于它包含的信息的性质。一些安全专家建议,用户只有在密码被泄露时才更改密码,因为如果系统使密码管理变得过于困难,他们就会开始做不安全的事情,比如将密码保存在键盘下,这对某些组织来说比拥有更大的威胁从不更改密码的用户。如果您的网站是论坛或评论网站或类似性质的网站,

一种可能的哈希函数:

function the_awesomest_hash($password)
{
    $salt1 = "awesomesalt!";
    $password = $salt1 . $password;
    for($i = 0; $i < 10000; $i++)
    {
        $password = hash('sha512', $password);
    }
    // Some time has passed, and you have added to your hash function
    $salt2 = "niftysalt!";
    $password = $salt2 . $password;
    for($i = 0; $i < 10000; $i++)
    {
        $password = hash('futuresuperhash1024', $password);
    }
    return $password;
}

现在,为了更新数据库中已有的所有密码,您可以通过以下函数运行它们:

function update_hash($password)
{
    // This is the last part of your the_awesomest_hash() function
    $salt2 = "niftysalt!";
    $password = $salt2 . $password;
    for($i = 0; $i < 10000; $i++)
    {
        $password = hash('futuresuperhash1024', $password);
    }
    return $password;
}

我喜欢编写自己的哈希函数,因为在更新它们时更容易跟踪到底发生了什么。

于 2012-08-26T19:03:55.643 回答
1

经过对 PHP 的一些研究,尤其是随机数生成,使用 PHP 安全加密的唯一方法是使用 OpenSSL 包装器。尤其是 mcrypt 的创建者是一群白痴,看看他们的示例中的 not how to perform cryptography 的例子:

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$text = "Meet me at 11 o'clock behind the monument.";
echo strlen($text) . "\n";

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
echo strlen($crypttext) . "\n";

请注意,默认情况下 MCRYPT_RAND 没有很好地播种。此外,仅上述代码至少有大约 5 个错误,他们不会修复它。

[编辑] 修改后的示例见下文。请注意,此示例也不是很安全(如上所述)。此外,通常你不应该加密密码......

# the key should be random binary, use scrypt, bcrypt or PBKDF2 to convert a string into a key
# key is specified using hexadecimals
$key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
echo "Key size (in bits): " . $key_size * 8 . "\n";
$plaintext = "This string was AES-256 / CBC / ZeroBytePadding encrypted.";
echo "Plain text: " . $plain_text . "\n";
$ciphertext_base64 = encryptText($key, $plaintext);
echo  $ciphertext_base64 . "\n";


function encryptText(string $key_hex, string $plaintext) {

    # --- ENCRYPTION ---


    # show key size use either 16, 24 or 32 byte keys for AES-128, 192 and 256 respectively
    $key_size =  strlen($key);


    # create a random IV to use with CBC encoding
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

    # use an explicit encoding for the plain text
    $plaintext_utf8 = utf8_encode($plaintext);

    # creates a cipher text compatible with AES (Rijndael block size = 128) to keep the text confidential 
    # only suitable for encoded input that never ends with value 00h (because of default zero padding)
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext_utf8, MCRYPT_MODE_CBC, $iv);

    # prepend the IV for it to be available for decryption
    $ciphertext = $iv . $ciphertext;

    # encode the resulting cipher text so it can be represented by a string
    $ciphertext_base64 = base64_encode($ciphertext);

    return $ciphertext_base64;
}


# === WARNING ===

# Resulting cipher text has no integrity or authenticity added
# and is not protected against padding oracle attacks.

# --- DECRYPTION ---

$ciphertext_dec = base64_decode($ciphertext_base64);

# retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_dec = substr($ciphertext_dec, 0, $iv_size);

# retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);

# may remove 00h valued characters from end of plain text
$plaintext_utf8_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

echo  $plaintext_utf8_dec . "\n";
于 2012-08-20T12:38:30.463 回答
-1

到目前为止,我知道保存密码的最佳方法是使用 joomla 中使用的加盐哈希。您还可以将额外的密钥与传统的 base64 一起添加到 md5 哈希中。我前段时间写了一个类似的脚本,试图找到它但找不到。

Joomla 使用加盐的 md5 密码。获取您提供的哈希密码:30590cccd0c7fd813ffc724591aea603:WDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb

如果您的密码是“密码”,则: md5('passwordWDmIt53GwY2X7TvMqDXaMWJ1mrdZ1sKb') = 30590cccd0c7fd813ffc724591aea603

所以,把你的密码。生成一个随机的 32 个字符的字符串。计算与随机字符串连接的密码的 md5。将 md5 结果加上 : 加上随机的 32 个字符串存储在数据库中。

于 2012-08-24T21:03:12.140 回答