diff options
author | Yorhel <git@yorhel.nl> | 2014-10-15 12:34:40 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2014-10-15 12:34:40 +0200 |
commit | 2640d51d745bdbd85bf52f92aa4ed46253ccf99d (patch) | |
tree | 8536f89531cd6bb8e12fcd1f68dd16b530b491f4 /lib | |
parent | a1b4da1d3ae9e6ed9326df41f9831be81f6b839a (diff) |
SQL: Merge users.(passwd|salt) in one column + document values
It doesn't make a whole lot to separate the hashed password and the salt
from each other, you need both to do anything with them, and from the
database perspective they're both completely opaque strings only usable
for direct comparison with other hashed strings.
This change is mostly as preparation for switching to a proper key
derivation function (sha256 isn't...) and to add support for longer
and/or binary salt.
Because the passwd field now needs to be interpreted in Perl, it's being
passed around as a binary string rather than a hex-encoded value.
API login is broken in this commit. I'll get to that.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/VNDB/DB/Users.pm | 16 | ||||
-rw-r--r-- | lib/VNDB/Handler/Users.pm | 14 | ||||
-rw-r--r-- | lib/VNDB/Util/Auth.pm | 43 |
3 files changed, 32 insertions, 41 deletions
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm index e7bf85ca..9082325a 100644 --- a/lib/VNDB/DB/Users.pm +++ b/lib/VNDB/DB/Users.pm @@ -55,10 +55,7 @@ sub dbUserGet { my @select = ( qw|id username c_votes c_changes c_tags|, q|extract('epoch' from registered) as registered|, - $o{what} =~ /extended/ ? ( - qw|mail perm salt ign_votes|, - q|encode(passwd, 'hex') AS passwd| - ) : (), + $o{what} =~ /extended/ ? qw|mail perm ign_votes passwd| : (), $o{what} =~ /hide_list/ ? 'up.value AS hide_list' : (), $o{what} =~ /notifycount/ ? '(SELECT COUNT(*) FROM notifications WHERE uid = u.id AND read IS NULL) AS notifycount' : (), @@ -119,10 +116,11 @@ sub dbUserEdit { my %h; defined $o{$_} && ($h{$_.' = ?'} = $o{$_}) - for (qw| username mail perm salt ign_votes email_confirmed |); - $h{'passwd = decode(?, \'hex\')'} = $o{passwd} + for (qw| username mail perm ign_votes email_confirmed |); + $h{'passwd = decode(?, \'hex\')'} = unpack 'H*', $o{passwd} if defined $o{passwd}; + return if scalar keys %h <= 0; return $s->dbExec(q| UPDATE users @@ -132,11 +130,11 @@ sub dbUserEdit { } -# username, pass(ecrypted), salt, mail, [ip] +# username, pass(ecrypted), mail, [ip] sub dbUserAdd { my($s, @o) = @_; - $s->dbRow(q|INSERT INTO users (username, passwd, salt, mail, ip) VALUES(?, decode(?, 'hex'), ?, ?, ?) RETURNING id|, - @o[0..3], $o[4]||$s->reqIP)->{id}; + $s->dbRow(q|INSERT INTO users (username, passwd, mail, ip) VALUES(?, decode(?, 'hex'), ?, ?) RETURNING id|, + $o[0], unpack('H*', $o[1]), $o[2], $o[3]||$s->reqIP)->{id}; } diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm index 271561af..bcf94fb4 100644 --- a/lib/VNDB/Handler/Users.pm +++ b/lib/VNDB/Handler/Users.pm @@ -208,8 +208,9 @@ sub newpass { if(!$frm->{_err}) { my %o; my $token; - ($token, $o{passwd}, $o{salt}) = $self->authPrepareReset(); + ($token, $o{passwd}) = $self->authPrepareReset(); $self->dbUserEdit($u->{id}, %o); + #warn "$self->{url}/u$u->{id}/setpass?t=$token"; $self->mail(mt('_newpass_mail_body', $u->{username}, "$self->{url}/u$u->{id}/setpass?t=$token"), To => $frm->{mail}, From => 'VNDB <noreply@vndb.org>', @@ -254,7 +255,7 @@ sub setpass { $t = $t->{t}; my $u = $self->dbUserGet(uid => $uid, what => 'extended')->[0]; - return $self->resNotFound if !$u || !$self->authValidateReset($u, $t); + return $self->resNotFound if !$u || !$self->authValidateReset($u->{passwd}, $t); my $frm; if($self->reqMethod eq 'POST') { @@ -267,7 +268,7 @@ sub setpass { if(!$frm->{_err}) { my %o = (email_confirmed => 1); - ($o{passwd}, $o{salt}) = $self->authPreparePass($frm->{usrpass}); + $o{passwd} = $self->authPreparePass($frm->{usrpass}); $self->dbUserEdit($uid, %o); return $self->authLogin($u->{username}, $frm->{usrpass}, "/u$uid"); } @@ -307,8 +308,9 @@ sub register { push @{$frm->{_err}}, 'oneaday' if !$frm->{_err} && $self->dbUserGet(ip => $ip =~ /:/ ? "$ip/48" : $ip, registered => time-24*3600)->[0]{id}; if(!$frm->{_err}) { - my($token, $pass, $salt) = $self->authPrepareReset(); - my $uid = $self->dbUserAdd($frm->{usrname}, $pass, $salt, $frm->{mail}); + my($token, $pass) = $self->authPrepareReset(); + my $uid = $self->dbUserAdd($frm->{usrname}, $pass, $frm->{mail}); + warn "$self->{url}/u$uid/setpass?t=$token"; $self->mail(mt('_register_mail_body', $frm->{usrname}, "$self->{url}/u$uid/setpass?t=$token"), To => $frm->{mail}, From => 'VNDB <noreply@vndb.org>', @@ -388,7 +390,7 @@ sub edit { $o{perm} |= $self->{permissions}{$_} for(@{ delete $frm->{perms} }); } $o{mail} = $frm->{mail}; - ($o{passwd}, $o{salt}) = $self->authPreparePass($frm->{usrpass}) if $frm->{usrpass}; + $o{passwd} = $self->authPreparePass($frm->{usrpass}) if $frm->{usrpass}; $o{ign_votes} = $frm->{ign_votes} ? 1 : 0 if $self->authCan('usermod'); $self->dbUserEdit($uid, %o); $self->dbSessionDel($uid) if $frm->{usrpass}; diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm index 89cbd215..63812d36 100644 --- a/lib/VNDB/Util/Auth.pm +++ b/lib/VNDB/Util/Auth.pm @@ -5,7 +5,7 @@ package VNDB::Util::Auth; use strict; use warnings; use Exporter 'import'; -use Digest::SHA qw|sha1_hex sha256_hex|; +use Digest::SHA qw|sha1 sha1_hex sha256|; use Time::HiRes; use Encode 'encode_utf8'; use POSIX 'strftime'; @@ -30,7 +30,7 @@ sub authInit { my $token = substr($cookie, 0, 40); my $uid = substr($cookie, 40); $self->{_auth} = $uid =~ /^\d+$/ && $self->dbUserGet(uid => $uid, session => $token, what => 'extended notifycount prefs')->[0]; - # update the sessions.lastused column if lastused < now()'6 hours' + # update the sessions.lastused column if lastused < now()-'6 hours' $self->dbSessionUpdateLastUsed($uid, $token) if $self->{_auth} && $self->{_auth}{session_lastused} < time()-6*3600; return $self->resCookie(auth => undef) if !$self->{_auth}; } @@ -103,9 +103,9 @@ sub _authCheck { return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass; my $d = $self->dbUserGet(username => $user, what => 'extended notifycount')->[0]; - return 0 if !$d->{id} || $d->{salt} =~ /^ *$/; + return 0 if !$d->{id} || length $d->{passwd} != 41; - if(_authEncryptPass($self, $pass, $d->{salt}) eq $d->{passwd}) { + if($self->authPreparePass($pass, substr $d->{passwd}, 0, 9) eq $d->{passwd}) { $self->{_auth} = $d; return 1; } @@ -114,43 +114,34 @@ sub _authCheck { } -# Encryption algorithm for user passwords -# Arguments: self, pass, salt -# Returns: encrypted password (in hex) -sub _authEncryptPass { - my($self, $pass, $salt, $bin) = @_; - return sha256_hex($self->{global_salt} . encode_utf8($pass) . encode_utf8($salt)); -} - - # Prepares a plaintext password for database storage -# Arguments: pass -# Returns: list (pass, salt) +# Arguments: pass, optionally salt +# Returns: encrypted password (as a binary string) sub authPreparePass { - my($self, $pass) = @_; - my $salt = join '', map chr(rand(93)+33), 1..9; - my $hash = _authEncryptPass($self, $pass, $salt); - return ($hash, $salt); + my($self, $pass, $salt) = @_; + $salt ||= encode_utf8(join '', map chr(rand(93)+33), 1..9); + return $salt.sha256($self->{global_salt} . encode_utf8($pass) . $salt); } # Generates a random token that can be used to reset the password. -# Returns: token, token-encrypted, salt +# Returns: token (hex string), token-encrypted (binary string) sub authPrepareReset { my $self = shift; my $token = sha1_hex(join('', Time::HiRes::gettimeofday()) . join('', map chr(rand(93)+33), 1..9)); my $salt = join '', map chr(rand(93)+33), 1..9; - my $token_e = sha1_hex(lc($token).$salt); - return ($token, $token_e, $salt); + my $token_e = encode_utf8($salt) . sha1(lc($token).$salt); + return ($token, $token_e); } # Checks whether the password reset token is valid. -# Arguments: $u obj, token +# Arguments: passwd (binary string), token (hex string) sub authValidateReset { - my($self, $u, $t) = @_; - return 0 if !$u->{salt} || !$u->{passwd} || length $u->{passwd} != 40 - || lc sha1_hex(lc($t).$u->{salt}) ne lc $u->{passwd}; + my($self, $passwd, $token) = @_; + return 0 if length $passwd != 29; + my $salt = substr $passwd, 0, 9; + return 0 if $salt.sha1(lc($token).$salt) ne $passwd; return 1; } |