From a5cf07a181063675e8b31f668c1d0803b382f926 Mon Sep 17 00:00:00 2001 From: Rudis Muiznieks Date: Sun, 19 Jul 2020 10:04:08 -0500 Subject: [PATCH] replaced rand() with cryptographically secure alternative --- Dockerfile | 6 +++--- server.pl | 32 +++++++++++++------------------- test/run.sh | 2 +- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8becd80..d4cd2a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,14 @@ from alpine:latest -run apk add wget gnupg sqlite build-base perl perl-dev perl-app-cpanminus -run cpanm --notest IPC::Run DBD::SQLite Net::DNS::Resolver Crypt::Eksblowfish::Bcrypt JSON URI::Escape HTML::Entities String::ShellQuote Net::Server HTTP::Server::Simple +run apk add wget gnupg sqlite unzip build-base perl perl-dev perl-app-cpanminus +run cpanm --notest IPC::Run DBD::SQLite Net::DNS::Resolver Crypt::Eksblowfish::Bcrypt JSON URI::Escape HTML::Entities String::ShellQuote Net::Server HTTP::Server::Simple Crypt::Random run mkdir -p /opt/data/plans copy schema.sql /opt/data run cat /opt/data/schema.sql | sqlite3 /opt/data/users.db run rm /opt/data/schema.sql -run apk del build-base perl-dev perl-app-cpanminus wget sqlite +run apk del build-base perl-dev perl-app-cpanminus wget sqlite unzip copy server.pl /opt workdir /opt diff --git a/server.pl b/server.pl index 7c531ab..c009bee 100644 --- a/server.pl +++ b/server.pl @@ -55,6 +55,7 @@ if (defined $ENV{'LOCAL_DOMAINS'}) { use File::Temp qw(tempfile); use Fcntl qw(:flock); use Net::DNS::Resolver; + use Crypt::Random qw(makerandom_itv); use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash en_base64); use MIME::Base64 qw(decode_base64); use POSIX qw(strftime); @@ -92,7 +93,7 @@ if (defined $ENV{'LOCAL_DOMAINS'}) { sub handle_request { my ($self, $cgi) = @_; # assign a random request id for anonymous logging - my $req_id = util_req_id(); + my $req_id = util_token(12); $cgi->param('request_id', $req_id); my $path = $cgi->path_info(); my $method = $cgi->request_method(); @@ -206,7 +207,7 @@ EOF : "INSERT INTO users (password, pw_token, pw_token_expires, email) values (?, ?, datetime('now', '+$pw_token_expiration_minutes minutes'), ?)"; my $crypted = util_bcrypt($password); my $sth = util_get_dbh()->prepare($query); - $sth->execute($crypted, util_token(), $email); + $sth->execute($crypted, util_token(24), $email); die $sth->errstr if $sth->err; # TODO: send email print_json_response($cgi, 200, {email => $email}); @@ -244,10 +245,10 @@ EOF print_response($cgi, 401, $not_authorized); } else { my $sth = util_get_dbh()->prepare("UPDATE users SET token=?, token_expires=datetime('now', ?) WHERE email=?"); - my $token = util_token(); + my $token = util_token(24); my $expires = $cgi->param('expires'); my $minutes = $auth_token_default_expiration_minutes; - if ($expires =~ /^\d+$/) { + if (defined $expires && $expires =~ /^\d+$/) { $minutes = int($expires); if ($minutes <= 0) { $minutes = $auth_token_default_expiration_minutes; @@ -282,7 +283,7 @@ EOF } elsif (defined $user->{'pw_token_expires'} && $user->{'pw_token_expires'} >= time) { print_json_response($cgi, 429, {error => "Wait $pw_token_expiration_minutes between this type of request."}); } else { - my $token = util_token(); + my $token = util_token(24); my $sth = util_get_dbh()->prepare("UPDATE users SET pw_token=?, pw_token_expires=datetime('now', '+10 minutes') WHERE email=?"); $sth->execute($token, $email); die $sth->errstr if $sth->err; @@ -426,7 +427,7 @@ EOF my $msg = shift; my $timestamp = strftime("%Y-%m-%d %H:%M:%S", localtime(time())); if (!defined $_log) { - open($_log, '>>', $log_file); + open($_log, '>>', $log_file) or die $!; binmode($_log, ':unix'); } print $_log "$timestamp $msg\n"; @@ -470,7 +471,7 @@ EOF sub util_salt { my $itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; my $salt = ''; - $salt .= substr($itoa64,int(rand(64)),1) while length($salt) < 16; + $salt .= substr($itoa64,int(makerandom_itv(Strength => 0, Upper => 64)),1) while length($salt) < 16; return $salt; } @@ -504,20 +505,13 @@ EOF # generate an authorization token sub util_token { - my $itoa62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + my $length = shift; + my $chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; my $token = ''; - $token .= substr($itoa62,int(rand(62)),1) while length($token) < 24; + $token .= substr($chars,int(makerandom_itv(Strength => 0, Upper => 62)),1) while length($token) < $length; return $token; } - # generate a random request id - sub util_req_id { - my $itoa36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - my $id = ''; - $id .= substr($itoa36,int(rand(36)),1) while length($id) < 8; - return $id; - } - # get a user from the database by email sub util_get_user { my $email = shift; @@ -564,7 +558,7 @@ EOF if (-f "$basename.plan") { my $details = {}; - open(my $plan_file, '<', "$basename.plan"); + open(my $plan_file, '<', "$basename.plan") or die $!; flock($plan_file, LOCK_SH); my $mtime = (stat($plan_file))[9]; my $timestamp = strftime("%a, %d %b %Y %H:%M:%S %z", localtime($mtime)); @@ -574,7 +568,7 @@ EOF close($plan_file); if (-f "$basename.asc") { - open(my $sig_file, '<', "$basename.asc"); + open(my $sig_file, '<', "$basename.asc") or die $!; flock($sig_file, LOCK_SH); local $/; $details->{'signature'} = <$sig_file>; diff --git a/test/run.sh b/test/run.sh index 4c6b69e..d045ee1 100755 --- a/test/run.sh +++ b/test/run.sh @@ -108,7 +108,7 @@ if [ -z "$USE_DOCKER" ]; then DATABASE="$BASEDIR/data/test.db" \ PLAN_DIR="$BASEDIR/data/plans" \ SENDMAIL=/usr/bin/true \ - perl "$BASEDIR/../server.pl" -d >>/dev/null 2>>/dev/null + perl "$BASEDIR/../server.pl" -d >>/dev/null else docker build -t dotplan-online-test "$BASEDIR/.." docker run --name dotplan_online_test -d --rm \