使用PHP来encryption和解密密码的最佳方法?
可能重复:
PHP的双向encryption:我需要存储可以检索的密码
我计划在我的网站上存储我的用户的外部账户信息,也就是用户名和密码等等。我想保证信息的安全,但是我知道如果我把他们的信息散列出来,我不能检索它以备后用。
Base64是可以解密的,所以没有意义使用它。 我的想法是在用户解密之前和之后通过这种方式来打乱用户,如果你尝试解密,你会看到一些有趣的文字。 有没有一个PHP函数接受值将会使一个string的唯一争夺,并解除它后来的价值重新计算?
有什么build议么?
你不应该encryption密码,而应该使用像bcrypt这样的algorithm对它们进行哈希处理。 这个答案解释了如何在PHP中正确实现密码哈希 。 不过,这里是你将如何encryption/解密:
$key = 'password to (en/de)crypt'; $string = ' string to be encrypted '; // note the spaces
encryption:
$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 ) );
解密:
$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" );
警告 :上面的例子encryption信息,但它不authentication密文,以防止篡改。 您不应该依赖未经身份validation的encryption来安全性 ,尤其是因为所提供的代码容易受到填充Oracle攻击的影响。
也可以看看:
- https://stackoverflow.com/a/30189841/2224584
- https://stackoverflow.com/a/30166085/2224584
- https://stackoverflow.com/a/30159120/2224584
另外,不要只为密钥使用“密码”。 encryption密钥是随机string。
在3v4l.org演示 :
echo 'Encrypted:' . "\n"; var_dump($encrypted); // "m1DSXVlAKJnLm7k3WrVd51omGL/05JJrPluBonO9W+9ohkNuw8rWdJW6NeLNc688=" echo "\n"; echo 'Decrypted:' . "\n"; var_dump($decrypted); // " string to be encrypted "
安全警告 :这个类是不安全的。 它使用Rijndael256-ECB ,这在语义上是不安全的。 仅仅因为“它有效”并不意味着“它是安全的”。 此外,由于没有使用适当的填充,它会剥去尾部空白。
最近发现这个类,它像梦一样工作!
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); } }
并称之为:
$str = "My secret String"; $converter = new Encryption; $encoded = $converter->encode($str ); $decoded = $converter->decode($encoded); echo "$encoded<p>$decoded";
安全警告:此代码不安全。
工作示例
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);
有一件事你应该很清楚在处理encryption时:
试图聪明,发明自己的东西通常会给你一些不安全的东西。
您可能最好使用PHP附带的encryption扩展之一。
这只会给你边际保护。 如果攻击者可以在你的应用程序中运行任意代码,那么他们可以以与应用程序完全相同的方式获取密码。 如果您将密钥存储在文件中,并使用该密钥encryption到数据库并在出口解密,那么仍然可以获得一些SQL注入攻击和错误放置的数据库备份的保护。 但是你应该使用bindparams来完全避免SQL注入的问题。
如果决定encryption,你应该使用一些高级的encryption库,否则你会错误的。 您必须正确设置密钥设置,消息填充和完整性检查,否则您的所有encryption工作都无用。 GPGME是一个很好的select。 Mcrypt的水平太低,你可能会错误的。
安全警告 :此代码不安全 。 除了易受到select密文攻击之外,对
unserialize()
依赖使得它易受PHP Object Object的影响。
处理一个string/数组我使用这两个函数:
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存储/发送一个string或数组,因为在encryption之前串行化string/数组。
看看mycrypt(): http ://us.php.net/manual/en/book.mcrypt.php
如果您使用的是Postgres,则可以使用pgcrypto进行数据库级别的encryption。 (使search和sorting更容易)
即使您有权访问代码,对数据库中的数据进行encryption/解密的最好方法是使用2个不同的传递给每个用户的私人密码( user-pass
)和所有用户的私人密码( system-pass
)。
脚本
-
user-pass
与md5一起存储在数据库中,用于validation每个用户login到系统。 这个用户通行证对于每个用户是不同的。 - 数据库中的每个用户条目在md5中都有一个用于encryption/解密数据的
system-pass
。 这个系统传递对于每个用户是相同的。 - 每当用户被从系统中删除,所有在旧系统中encryption的数据都必须在新的系统下再次encryption,以避免安全问题。