summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2014-10-15 12:34:40 +0200
committerYorhel <git@yorhel.nl>2014-10-15 12:34:40 +0200
commit2640d51d745bdbd85bf52f92aa4ed46253ccf99d (patch)
tree8536f89531cd6bb8e12fcd1f68dd16b530b491f4 /lib
parenta1b4da1d3ae9e6ed9326df41f9831be81f6b839a (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.pm16
-rw-r--r--lib/VNDB/Handler/Users.pm14
-rw-r--r--lib/VNDB/Util/Auth.pm43
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;
}