有没有更好的方法在 perl cgi 脚本中提供合适的密码散列?

Is there a better way to provide decent password hashing in a perl cgi script?

好吧,我花了几天时间查看这里和其他地方的代码,我只是不断地将各个部分拼凑在一起。这个脚本对我有用,我仍在审查安全漏洞,但我已尽职尽责地尝试创建它。我不是 perl 天才,对 regexp 有点生疏。

此脚本的目的将用作聊天室的嵌入式密钥,我很可能会使用原始的 crypt 来分支出第二个密钥,因此需要额外的步骤和混淆,以防有人开始拆开各层生成的打印页面。我不想将用于登录的密钥与嵌入式密钥存储在数据库中,但出于显而易见的原因,它们需要相同的根密码。我可能在太多地方使用了过多的清理字符,但我想为什么不呢,以防我将其作为一个单独的文件。

use Digest::SHA qw(hmac_sha256_hex);

sub passCrypt {
    chomp(my $userin=$_[0]);            # first passed var is username
    chomp(my $passin=$_[1]);            # second passed var is plaintext password
    $userin = substr $userin, 0, 24;    # protect from DoS attacks by limiting length of input
    $passin = substr $passin, 0, 64;
    $passin =~ tr/\\?\/\<\>/XPZQJ/;    # clean password replacing \ ? / < > with safe chars
    $passin =~ s/0x[0-9a-fA-F]//g;      # clean password from hex 0x0 - 0xF
    $userin =~ s/[^\w]//g;              # clean username from any non-word characters (a-z A-Z 0-9 _)
    $userin =~ s/[_]//g;                # clean username from the _ (for salt)
    $userin = lc($userin);              # all lowercase username
    my $salt = substr($userin, 1, 1) . substr($userin, -2, 1);  # make salt from first and last of username
    my $clnpass = crypt($passin,$salt);  # use basic crypt to prevent plain text password being utilized beyond this point
    $clnpass = substr $clnpass, 2;  # strip salt from beginning of pass
    $clnpass =~ tr/\/\\!\@\#$\%\^\&\*\(\)\[\]\{\}\|\?\=\+\-\_\<\>\:\;\"\'\`\,\./1234567890123456789012345678901/;  # make pass filename safe (replacement to keep consistent length)
    $clnpass =~ tr/abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ/WXYZwxcd123efghFGHIJij4ABlmnopqNOPQRrstuCDEKL567MSkyzTUVa890/;  # added obfuscation
    $clnpass = reverse($clnpass);  # more obfuscation
    my $cryptpass = hmac_sha256_hex($clnpass, chr(0x0b) x 32);  # final crypt with sha256
    return $cryptpass; 
}

我把它放在这里以防其他人在搜索时遇到与我类似的问题,并希望得到很多改进建议。有没有更好的方法在 perl cgi 脚本中提供合适的密码散列?我首先查看了 Crypt::Eksblowfish::Bcrypt,但我的主机没有那个包。提前致谢。

编辑:有人向我澄清,我所做的只是散列,所以我替换了上述文本。

您代码中的安全性基本上依赖于 crypthmac_sha256。剩下的就是混淆视听。鉴于该脚本可能与密码散列位于同一系统(这里没有加密,只是散列),混淆实际上不太可能帮助抵御攻击者 - 任何获得密码散列的人也可能也可以访问您的代码.

剩下的 crypthmac_sha256 都是快速散列函数,因此本质上您创建了一个用于密码散列的快速函数。但是使用快速哈希意味着攻击者也可以快速暴力破解任何现有的哈希。这就是为什么足够慢的密码哈希是一个重要的设计标准。

有关如何正确散列密码的更多详细信息,请参阅 How to securely hash passwords?。请不要发明你自己的方法,而是使用现有的方法。在安全方面,最好依靠经过验证的方法,而不是发明自己的方法,这对你来说可能看起来安全,但对其他人来说却不安全。

所以采纳了 Steffen 的所有建议,这就是我最终得到的,将改变我实际使用的一些细节,因为这可能与我将使用的相似,坦率地说,如果我继续使用,安全性似乎很愚蠢发布我的脚本。

use Digest::SHA qw(hmac_sha512_hex hmac_sha256_base64);
use Crypt::PBKDF2;

my $hash = &MakeHash($username,$password); #test
print &MakeSessionID($hash); #test

sub MakeHash {
  chomp(my $userin=$_[0]);  #i do this as a habbit when I work with files, I know this info should not be coming from a file, I will clean it up later
  chomp(my $passin=$_[1]);
  $userin = substr $userin, 0, 24;  #keeping this to prevent accidents while I keep working
  $passin = substr $passin, 0, 64;
  my $hashkey = hmac_sha256_base64($userin,$userin);
  my $pbkdf2 = Crypt::PBKDF2->new(hash_class => 'HMACSHA1',iterations => 200000,output_len => 24); #these are not the values im actually using but close enough
  my $result = $pbkdf2->PBKDF2_base64($hashkey,$passin);
  return $result; }

sub MakeSessionID {
  chomp(my $passin=$_[0]);
  my $salt = time*2;
  my $result = hmac_sha512_hex($passin, $salt);
  return $result; }

显然缺少我利用它的功能但是是的