我正在创建一个存储密码的应用程序,用户可以检索和查看该密码。密码用于硬件设备,因此无法检查哈希值。
我需要知道的是:
如何在PHP中加密和解密密码?
什么是最安全的密码加密算法?
我在哪里存储私钥?
除了存储私钥之外,要求用户在需要解密密码的任何时间都输入私钥是一个好主意吗?(可以信任此应用程序的用户)
密码可以通过什么方式被盗和解密?我需要注意什么?
就个人而言,我会mcrypt
像其他人一样使用。但是还有更多需要注意的地方…
请参阅下面的强大课程,该课程可以为您解决所有问题:
最安全
?任何一位。如果要加密,最安全的方法是防止信息泄露漏洞(XSS,远程包含等)。如果失败,攻击者最终可以破解加密(没有密钥,没有任何加密是100%不可逆的-
正如@NullUserException指出的那样,这并不完全正确。有一些无法破解的加密方案,例如OneTimePad) 。
我要做的是使用3个键。一个是用户提供的,一个是特定于应用程序的,另一个是特定于用户的(例如盐)。特定于应用程序的密钥可以存储在任何地方(在Web根目录之外的配置文件中,环境变量中等等)。用户专用密码将存储在数据库中加密密码旁边的一列中。用户提供的将不会被存储。然后,您将执行以下操作:
$key = $userKey . $serverKey . $userSuppliedKey;
这样做的好处是,其中任何两个密钥都可以被破坏,而不会破坏数据。如果发生SQL注入攻击,则可以获取$userKey
,但不能获取其他2。如果存在本地服务器利用,则可以获取$userKey
和$serverKey
,但不能获取$userSuppliedKey
。如果他们用扳手殴打用户,他们可以拿到$userSuppliedKey
,但不能拿到其他2(但是,如果用扳手殴打用户,无论如何你都来不及了)。
绝对。实际上,那是我要做的唯一方法。否则,您需要以持久存储格式(共享内存(例如APC或内存缓存)或会话文件)存储未加密的版本。这会使您面临更多的折衷。除本地变量外,切勿将未加密版本的密码存储在任何其他内容中。
系统的任何形式的破坏都会让他们查看加密的数据。如果他们可以注入代码或访问您的文件系统,则可以查看解密的数据(因为他们可以编辑对数据进行解密的文件)。任何形式的重播或MITM攻击也将使他们能够完全访问所涉及的密钥。嗅探原始HTTP流量也将为他们提供密钥。
对所有流量使用SSL。并确保服务器上没有任何漏洞(CSRF,XSS,SQL注入,权限提升,远程执行代码等)。
编辑: 这是一个强加密方法的PHP类实现:
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class Encryption {
/**
* @var string $cipher The mcrypt cipher to use for this instance
*/
protected $cipher = '';
/**
* @var int $mode The mcrypt cipher mode to use
*/
protected $mode = '';
/**
* @var int $rounds The number of rounds to feed into PBKDF2 for key generation
*/
protected $rounds = 100;
/**
* Constructor!
*
* @param string $cipher The MCRYPT_* cypher to use for this instance
* @param int $mode The MCRYPT_MODE_* mode to use for this instance
* @param int $rounds The number of PBKDF2 rounds to do on the key
*/
public function __construct($cipher, $mode, $rounds = 100) {
$this->cipher = $cipher;
$this->mode = $mode;
$this->rounds = (int) $rounds;
}
/**
* Decrypt the data with the provided key
*
* @param string $data The encrypted datat to decrypt
* @param string $key The key to use for decryption
*
* @returns string|false The returned string if decryption is successful
* false if it is not
*/
public function decrypt($data, $key) {
$salt = substr($data, 0, 128);
$enc = substr($data, 128, -64);
$mac = substr($data, -64);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
return false;
}
$dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);
$data = $this->unpad($dec);
return $data;
}
/**
* Encrypt the supplied data using the supplied key
*
* @param string $data The data to encrypt
* @param string $key The key to encrypt with
*
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
return $salt . $enc . $mac;
}
/**
* Generates a set of keys given a random salt and a master key
*
* @param string $salt A random string to change the keys each encryption
* @param string $key The supplied key to encrypt with
*
* @returns array An array of keys (a cipher key, a mac key, and a IV)
*/
protected function getKeys($salt, $key) {
$ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$keySize = mcrypt_get_key_size($this->cipher, $this->mode);
$length = 2 * $keySize + $ivSize;
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
$iv = substr($key, 2 * $keySize);
return array($cipherKey, $macKey, $iv);
}
/**
* Stretch the key using the PBKDF2 algorithm
*
* @see http://en.wikipedia.org/wiki/PBKDF2
*
* @param string $algo The algorithm to use
* @param string $key The key to stretch
* @param string $salt A random salt
* @param int $rounds The number of rounds to derive
* @param int $length The length of the output key
*
* @returns string The derived key.
*/
protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
$res = $tmp;
for ($j = 1; $j < $rounds; $j++) {
$tmp = hash_hmac($algo, $tmp, $key, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
protected function pad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$padAmount = $length - strlen($data) % $length;
if ($padAmount == 0) {
$padAmount = $length;
}
return $data . str_repeat(chr($padAmount), $padAmount);
}
protected function unpad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$last = ord($data[strlen($data) - 1]);
if ($last > $length) return false;
if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
return false;
}
return substr($data, 0, -1 * $last);
}
}
请注意,我正在使用PHP 5.6中添加的功能:hash_equals
。如果低于5.6,则可以使用此替代函数,该函数使用双重HMAC验证实现定时安全比较功能:
function hash_equals($a, $b) {
$key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}
用法:
$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);
然后,解密:
$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);
请注意,我$e2
第二次向您展示了不同的实例仍将正确解密数据。
现在,它是如何工作的/为什么要在另一个解决方案上使用它:
按键
按键不直接使用。而是通过标准PBKDF2派生来扩展密钥。
用于加密的密钥对于每个加密的文本块都是唯一的。因此,提供的密钥将成为“主密钥”。因此,此类为密码和身份验证密钥提供密钥轮换。
重要说明 ,该$rounds
参数配置为具有足够强度的真随机密钥(最少128位加密安全随机数)。如果要使用密码或非随机密钥(或者随机性较小,则CS随机性为128位),则 必须 增加此参数。我建议密码至少为10000(您能负担得起的越多越好,但是它将增加运行时的信息)…
数据的完整性
加密:
它使用mcrypt实际执行加密。我建议使用MCRYPT_BLOWFISH
或MCRYPT_RIJNDAEL_128
cypher和MCRYPT_MODE_CBC
模式。它足够强大,并且仍然相当快(加密和解密周期在我的计算机上大约需要1/2秒)。
现在,从第一个列表的第3点开始,将为您提供的功能是这样的:
function makeKey($userKey, $serverKey, $userSuppliedKey) {
$key = hash_hmac('sha512', $userKey, $serverKey);
$key = hash_hmac('sha512', $key, $userSuppliedKey);
return $key;
}
您可以在makeKey()
函数中对其进行拉伸,但是由于稍后将对其进行拉伸,因此这样做没有太大意义。
至于存储大小,取决于纯文本。Blowfish使用8字节的块大小,因此您将拥有:
因此,对于16个字符的数据源,将有16个字符的数据要加密。因此,由于填充,实际的加密数据大小为16个字节。然后为salt添加16个字节,为hmac添加64个字节,总存储大小为96个字节。因此充其量只有80个字符的开销,最糟糕的是87个字符的开销…
希望对您有帮助…
注意: 12/11/12:我刚刚使用更好的加密方法,使用更好的派生密钥并修复了MAC生成来更新此类。
问题内容: 我的应用程序(显然)使用唯一的ID来区分记录。除其他外,此UID在URL(例如)中传递。 虽然我显然已经在服务器端进行了验证,以确保客户端不会访问其他客户端的数据,但是我可以在PHP中使用一种双向加密方法来仅传递加密的UID(例如),以进一步减少机会有人认为“嘿,如果我增加这个整数会怎样?” TIA。 问题答案: 我认为,在ID旁边放置一个散列以确保其安全性,或者用多余的数据填充ID,
问题内容: 我们正在使用bcrypt来存储不需要解密的密码和数据。 应该采取什么措施来保护其他这样做的用户信息。对于此示例,假设我们不希望用户的真实姓名采用纯文本格式,以防有人获取数据库。 这是一些敏感数据,但还需要不时调用并以纯文本显示。有没有简单的方法可以做到这一点? 问题答案: 您可以使用crypto模块: 编辑 现在 createCipher 和 createDecipher 已被弃用,而
我试图在CBC模式下使用AES和Crypto++库加密(和解密)一个文件 以下是我已经做的: 为了加密文件,我以二进制模式打开它,并将内容转储为字符串: 当我将尝试解密此文件时,我如何分别提取iv和密文?IV是16字节长,但在这里我完全迷失了,我不知道如何做。
我正在寻找一些关于欧盟数据保护法和Firebase的建议,我在网上看了看,显然Firebase符合欧盟数据保护法,因为它在传输过程中安全地存储数据,并且Firebase服务器本身是加密的。所以如果是这样的话,我甚至需要在将个人数据发送到Firebase之前在本地加密它吗? 目前我正在加密所有个人数据,但问题是我希望有一个自动完成功能,可以在用户键入时搜索客户。现在这目前不起作用,因为搜索“sha”
问题内容: 我们可以在密钥存储区中加载多个证书和密钥吗? 是否总是需要仅加载对(即证书和密钥一起)? 如果密钥库具有多个证书和密钥,那么当Java SSL尝试建立作为服务器的连接时,将选择哪个证书和密钥? 问题答案: 尽管这取决于KeyStore类型,但是通常,您可以在单个存储中存储多个私钥和证书。 基于Java的服务器使用哪种密钥和证书组合取决于应用程序的实现方式。许多应用程序允许您使用 别名
我有一个premium storage account,在创建这个帐户后我就启用了加密,我找到了这个链接https://docs.microsoft.com/en-us/azure/storage/storage-service-encryption来检查blob是否加密。 现在,如果我使用Azure key vault加密OS盘和数据盘,它也用于保护静止数据,但加密存储帐户也会做同样的事情。有谁