【问题标题】:Best way to use PHP to encrypt and decrypt passwords? [duplicate]使用 PHP 加密和解密密码的最佳方法? [复制]
【发布时间】:2010-11-20 07:36:59
【问题描述】:

可能重复:
PHP 2-way encryption: I need to store passwords that can be retrieved

我计划在我的网站上为我的用户存储外国帐户信息,也就是 rapidshare 用户名和密码等...我想保证信息的安全,但我知道如果我对他们的信息进行哈希处理,我将无法检索它供以后使用。

Base64 是可解密的,所以直接使用它是没有意义的。 我的想法是扰乱用户并在它以这种方式进行base64化之前和之后通过,即使在你解密它之后,如果你尝试解密,你会得到一些看起来很有趣的文本。是否有一个接受值的 php 函数,该值将对字符串进行唯一的加扰,并在稍后重新输入值时对其进行解扰?

有什么建议吗?

【问题讨论】:

  • Base64?!哈哈哈,没有。使用 AES 加密并使用像 PassPack 一样的主密码。 phpaes.com
  • 网络服务器如何获取加密的用户凭据信息?是的,通过知道解密密码。如果我是一个可以访问你的盒子的攻击者,你的计划将如何阻碍我获取这些信息?您在这里得到的唯一东西是“加扰”,因此不会在您的盒子上以纯文本形式放置它。但它充其量只是通过默默无闻的安全性。
  • @stolsvik 这是针对 SQL 注入攻击的额外层。在您的数据库而不是您的服务器损坏的情况下,您仍然需要密钥来取回敏感信息。

标签: php mcrypt encryption scramble


【解决方案1】:

您不应加密密码,而应使用 bcrypt 之类的算法对密码进行哈希处理。 This answer explains how to properly implement password hashing in PHP. 不过,这里是加密/解密的方法:

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

加密:

$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"
);

警告:上面的例子对信息进行了加密,但是它没有对密文进行认证以防止被篡改。 You should not rely on unauthenticated encryption for security,特别是因为提供的代码容易受到填充 oracle 攻击。

另见:

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


Demo at 3v4l.org:

echo 'Encrypted:' . "\n";
var_dump($encrypted); // "m1DSXVlAKJnLm7k3WrVd51omGL/05JJrPluBonO9W+9ohkNuw8rWdJW6NeLNc688="

echo "\n";

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

【讨论】:

  • 其实不是“一些随机的”,而是“随机的,然后存储”。)
  • 我只是想知道$string 周围空格的重要性。是不是在加密前人为添加,增加破解难度?对不起,如果我错过了这一点;)
  • @wired00:不,一点也不!如果您解密一个加密字符串,您会注意到在字符串末尾添加了一些 NULL 字节。通常人们只是使用[r]trim() 来摆脱那些 NULL 字节,但在进程中也会删除空间。我添加了空格,以便人们可以测试并验证修剪解密字符串的正确方法是:$original = rtrim($decrypted, "\0"); - 注意\0。 =)
  • 神圣的地狱。即使作为如何存储密码的建议,这也很糟糕。它甚至不可逆(那么你为什么首先使用加密)......而且它不使用 bcrypt (你在第一行中提到)。至于如何加密数据,请查看此SO Answer,其中包含正确设置数据的适当工具...示例:您缺少填充(CBC 需要)...
  • 好吧,在深入查看代码之后,您根本没有存储密码。您使用密码作为密码密钥来存储其他数据。 所以这甚至不能回答问题。如果你这样做,你需要首先对密码使用密钥派生函数。类似PBKDF2...
【解决方案2】:

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

试图变得聪明并发明自己的东西通常会给你带来不安全感。

最好使用 PHP 附带的 cryptography extensions 之一。

【讨论】:

  • 这些并不总是直观易用,甚至不知道该使用哪一个。也许一些代码示例在这里很有价值。
  • 另外,不再推荐使用 Mcrypt。另见paragonie.com/blog/2015/05/…
【解决方案3】:

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

如果您使用的是 postgres,则可以使用 pgcrypto 进行数据库级加密。 (使搜索和排序更容易)

【讨论】:

【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

    安全警告:此代码不安全。除了容易受到选择密文攻击之外,它对unserialize() 的依赖使其容易受到 PHP 对象注入的攻击。

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

    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 存储/发送字符串或数组,因为字符串/数组在加密之前是序列化的。

    【讨论】:

    • 为什么使用md5($key)作为密钥而md5(md5($key))作为IV?
    【解决方案6】:

    安全警告:此代码不安全。

    工作示例

    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); 
    

    【讨论】:

    • 样本错误; IV随机两次,因为ECB根本不使用IV,首先使用ECB编码,不使用AES兼容代码,没有正确填充消息。是的,它的工作原理与 random number generator on xkcd 的工作原理相同。
    • 此答案不会生成安全密码文本,请不要使用。它仅在加密/解密的意义上起作用。
    【解决方案7】:

    安全警告:此类不安全。它使用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";
    

    【讨论】:

    • 谢谢!!我使用这个类来存储会话中的敏感数据,并稍作修改,添加了一个构造函数: function __construct() { $this->skey = md5(session_name()); }。我认为这样会更安全一点:)
    • 此答案不会生成安全密码文本,请不要使用。它仅在加密/解密的意义上起作用。
    • // you can change it 应该是// you MUST change it
    • 不,不,不。这是不好的。首先,它使用ECB模式,这不好。然后,它使用不安全的方法来生成 IV。它根本不会以安全的方式工作。相反,请查看this answer provides 之类的内容或仅使用库。 这不安全
    • PHP 5.6.x 的错误 Warning: mcrypt_encrypt(): Key of size 4 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported in
    【解决方案8】:

    即使您可以访问代码,加密/解密数据库中的数据的最佳方法是使用 2 个不同的通行证,每个用户的私人密码 (user-pass) 和所有用户的私人密码 (@987654322 @)。

    场景

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

    【讨论】:

      猜你喜欢
      • 2014-12-24
      • 2012-02-26
      • 2017-10-07
      • 1970-01-01
      • 1970-01-01
      • 2010-10-21
      • 2011-08-12
      • 2011-05-20
      • 2016-09-09
      相关资源
      最近更新 更多