关于mcrypt:使用PHP加密和解密密码的最佳方式?

Best way to use PHP to encrypt and decrypt passwords?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
PHP 2-way encryption: I need to store passwords that can be retrieved

我计划在我的网站上存储我的用户的外国帐户信息,即RapidShare用户名和密码等…我想保证信息的安全,但我知道如果我散列他们的信息,我就不能检索到它供以后使用。

base64是可解密的,所以使用它毫无意义。我的想法是在用户被加密之前和之后对其进行加密和传递,即使在你解密之后,如果你试图解密,你也会得到一些有趣的文本。是否有一个PHP函数接受值,这些值将对字符串进行唯一的置乱,并在稍后重新输入该值时对其进行去置乱?

有什么建议吗?


您不应该加密密码,而是应该使用bcrypt之类的算法散列密码。这个答案解释了如何在PHP中正确地实现密码散列。不过,以下是加密/解密的方法:

1
2
$key = 'password to (en/de)crypt';
$string = ' string to be encrypted '; // note the spaces

加密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$iv = mcrypt_create_iv(
    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
    MCRYPT_DEV_URANDOM
);

$encrypted = base64_encode(
    $iv .
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        $string,
        MCRYPT_MODE_CBC,
        $iv
    )
);

解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
$data = base64_decode($encrypted);
$iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

$decrypted = rtrim(
    mcrypt_decrypt(
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
        MCRYPT_MODE_CBC,
        $iv
    ),
   "\0"
);

警告:上面的示例加密信息,但不验证密文以防止篡改。您不应该依赖未经身份验证的加密来实现安全性,尤其是因为所提供的代码容易受到填充Oracle攻击。

参见:

  • https://stackoverflow.com/a/30189841/2224584
  • https://stackoverflow.com/a/30166085/2224584
  • https://stackoverflow.com/a/30159120/2224584

另外,不要只使用"密码"作为加密密钥。加密密钥是随机字符串。

3v4l.org上的演示:

1
2
3
4
5
6
7
8
9
10
echo 'Encrypted:' ."
"
;
var_dump($encrypted); //"m1DSXVlAKJnLm7k3WrVd51omGL/05JJrPluBonO9W+9ohkNuw8rWdJW6NeLNc688="

echo"
"
;

echo 'Decrypted:' ."
"
;
var_dump($decrypted); //" string to be encrypted"


Security Warning: This class is not secure. It's using Rijndael256-ECB, which is not semantically secure. Just because"it works" doesn't mean"it's secure". Also, it strips tailing spaces afterwards due to not using proper padding.

最近发现了这个班,它像一个梦!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Encryption {
    var $skey ="yourSecretKey"; // you can change it

    public  function safe_b64encode($string) {
        $data = base64_encode($string);
        $data = str_replace(array('+','/','='),array('-','_',''),$data);
        return $data;
    }

    public function safe_b64decode($string) {
        $data = str_replace(array('-','_'),array('+','/'),$string);
        $mod4 = strlen($data) % 4;
        if ($mod4) {
            $data .= substr('====', $mod4);
        }
        return base64_decode($data);
    }

    public  function encode($value){
        if(!$value){return false;}
        $text = $value;
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->skey, $text, MCRYPT_MODE_ECB, $iv);
        return trim($this->safe_b64encode($crypttext));
    }

    public function decode($value){
        if(!$value){return false;}
        $crypttext = $this->safe_b64decode($value);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->skey, $crypttext, MCRYPT_MODE_ECB, $iv);
        return trim($decrypttext);
    }
}

并称之为:

1
2
3
4
5
6
7
8
$str ="My secret String";

$converter = new Encryption;
$encoded = $converter->encode($str );
$decoded = $converter->decode($encoded);    

echo"$encoded<p>
$decoded"
;


Security warning: This code is not secure.

工作实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
define('SALT', 'whateveryouwant');

function encrypt($text)
{
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, SALT, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}

function decrypt($text)
{
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, SALT, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}

$encryptedmessage = encrypt("your message");
echo decrypt($encryptedmessage);


在处理加密时,您应该非常注意一件事:

试着变聪明,发明你自己的东西通常会让你有一些不安全的东西。

您最好使用PHP附带的一个加密扩展。


Securiy Warning: This code is insecure. In addition to being vulnerable to chosen-ciphertext attacks, its reliance on unserialize() makes it vulnerable to PHP Object Injection.

要处理字符串/数组,我使用以下两个函数:

1
2
3
4
5
6
7
8
9
function encryptStringArray ($stringArray, $key ="Your secret salt thingie") {
 $s = strtr(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), serialize($stringArray), MCRYPT_MODE_CBC, md5(md5($key)))), '+/=', '-_,');
 return $s;
}

function decryptStringArray ($stringArray, $key ="Your secret salt thingie") {
 $s = unserialize(rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode(strtr($stringArray, '-_,', '+/=')), MCRYPT_MODE_CBC, md5(md5($key))),"\0"));
 return $s;
}

它很灵活,因为字符串/数组在加密前是串行的,所以可以通过URL存储/发送字符串或数组。


这只会给你边际保护。如果攻击者可以在应用程序中运行任意代码,他们就可以以与应用程序完全相同的方式获取密码。如果在文件中存储一个密钥,并使用该密钥在访问数据库的过程中进行加密,在退出数据库的过程中进行解密,您仍然可以得到一些保护,以避免某些SQL注入攻击和数据库备份放错位置。但是您应该使用bindparams来完全避免SQL注入的问题。

如果决定加密,您应该为此使用一些高级加密库,否则会出错。您必须正确设置密钥、消息填充和完整性检查,否则您所做的所有加密工作都将毫无用处。GPGME就是一个很好的选择。mcrypt太低了,可能会出错。


查看mycrypt():http://us.php.net/manual/en/book.mcrypt.php

如果你使用的是Postgres,那么有PGCrypto用于数据库级加密。(使搜索和排序更容易)


即使您可以访问代码,在数据库中加密/解密数据的最佳方法是使用两种不同的密码:每个用户使用一个私有密码(user-pass,所有用户使用一个私有密码(system-pass)。

脚本

  • user-pass与md5一起存储在数据库中,用于验证每个用户登录系统。对于每个用户,此用户通行证是不同的。
  • 数据库中的每个用户条目在MD5中都有一个用于加密/解密数据的system-pass。此系统通行证对每个用户都是相同的。
  • 每当从系统中删除用户时,所有在旧系统密码下加密的数据都必须在新系统密码下再次加密,以避免出现安全问题。