Skip to content

Commit

Permalink
Item14507: Implement Foswiki::generateRandomChars
Browse files Browse the repository at this point in the history
Use a cryptographically secure pseudo-random number generator for
password salt, validation tokens, passwords etc.

Replace use of rand() with calls to generateRandomChars
  • Loading branch information
gac410 committed Oct 3, 2017
1 parent 90fa0a9 commit ef15a64
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 51 deletions.
56 changes: 56 additions & 0 deletions core/lib/Foswiki.pm
Expand Up @@ -2764,6 +2764,62 @@ sub expandMacrosOnTopicCreation {

=begin TML
---++ objectMethod generateRandomChars( $length [, $fromString] )
Generate a character string of length $length. If a $fromString is supplied,
use the characters in that string as the set of characters available for
the random string.
This routine attempts to use a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG)
If it is not availble, it falls back to the old Foswiki/TWiki algorithm, based upon the
perl rand() function.
=cut

my $CSPRNG;

sub generateRandomChars {

my $length = shift;
my $universe = shift;

# Provide a default if undefined
# $ and : illegal in htpasswd file, <># significant in URLs
$universe ||=
'_./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#%{}[]|';

unless ( defined $CSPRNG ) {
$CSPRNG = 0;
eval 'use Bytes::Random::Secure';
unless ($@) {
$CSPRNG = Bytes::Random::Secure->new(
NonBlocking => 1, # Use non-blocking source of randomness
Bits => 256 # This is the default entropy
);
}
}

if ($CSPRNG) {

# Use Secure::Random::Bytes
return $CSPRNG->string_from( $universe, $length, '' );

}
else {
# This is the original Foswiki / TWiki Salt algorithm.
# It mixes in the Login which is no longer recommended.
my @chars = split( //, $universe );
my $random;

foreach ( 1 .. $length ) {
$random .= $chars[ rand @chars ];
}
return $random;
}
}

=begin TML
---++ StaticMethod entityEncode( $text [, $extras] ) -> $encodedText
Escape special characters to HTML numeric entities. This is *not* a generic
Expand Down
1 change: 1 addition & 0 deletions core/lib/Foswiki/Contrib/core/DEPENDENCIES
Expand Up @@ -2,6 +2,7 @@ Algorithm::Diff,>=0,cpan,Required, for base Foswiki.
Archive::Tar,>=0,cpan,Optional, may be required by the Extensions Installer if command line tar or unzip is not available.
Archive::Zip,>=0,cpan,Optional, may be required by the Extensions Installer if command line tar or unzip is not available.
Authen::SASL,>=2.00,cpan,Optional, required for sending mail with Net::SMTP when server requires authentication.
Bytes::Random::Secure,>=0,cpan Optional, used for stronger random strings in passwords and other security tokens.
CGI,>=3.15,cpan,Required, for base Foswiki (Versions 2.89, 3.37, 3.43, 3.47 and 4.11-13 should be avoided for I18N. Most version from 3.15 and onwards should work. Note: As of perl 5.19.7, CGI is no longer shipped with perl core and must be installed using CPAN.)
CGI::Cookie,>=1.24,cpan,Required, Installs as part of CGI.
CGI::Session,>=4.30,cpan,Required, for configure and Sessions support, available from the CPAN archive.
Expand Down
3 changes: 2 additions & 1 deletion core/lib/Foswiki/UI/Register.pm
Expand Up @@ -737,7 +737,8 @@ sub _requireConfirmation {
$data->{LoginName} ||= $data->{WikiName};
$data->{webName} = $web;

$data->{"${type}Code"} = $data->{WikiName} . '.' . int( rand(99999999) );
$data->{"${type}Code"} =
$data->{WikiName} . '.' . Foswiki::generateRandomChars( 8, '0123456789' );

# SMELL: used for Register unit tests
$session->{DebugVerificationCode} = $data->{"${type}Code"};
Expand Down
7 changes: 1 addition & 6 deletions core/lib/Foswiki/Users.pm
Expand Up @@ -327,13 +327,8 @@ sub randomPassword {
( $Foswiki::cfg{MinPasswordLength} > 8 )
? $Foswiki::cfg{MinPasswordLength}
: 8;
my @chars = ( 'a' .. 'z', 'A' .. 'Z', 0 .. 9, '_', '.', '/' );
my $newpw;

foreach ( 1 .. $pwlen ) {
$newpw .= $chars[ rand @chars ];
}
return $newpw;
return Foswiki::generateRandomChars($pwlen);

}

Expand Down
50 changes: 7 additions & 43 deletions core/lib/Foswiki/Users/HtPasswdUser.pm
Expand Up @@ -562,10 +562,10 @@ sub encrypt {
my $salt;
$salt = $this->fetchPass($login) unless $fresh;
if ( $fresh || !$salt ) {
my @saltchars = ( 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '.', '/' );
$salt =
$saltchars[ int( rand( $#saltchars + 1 ) ) ]
. $saltchars[ int( rand( $#saltchars + 1 ) ) ];

$salt = Foswiki::generateRandomChars( 2,
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./'
);
}
return crypt( Foswiki::encode_utf8($passwd),
Foswiki::encode_utf8( substr( $salt, 0, 2 ) ) );
Expand All @@ -590,19 +590,7 @@ sub encrypt {
$salt = $this->fetchPass($login) unless $fresh;
if ( $fresh || !$salt ) {
$salt = '$apr1$';
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
foreach my $i ( 0 .. 7 ) {

# generate a salt not only from rand() but also mixing
# in the users login name: unecessary
$salt .= $saltchars[
(
int( rand( $#saltchars + 1 ) ) +
$i +
ord( substr( $login, $i % length($login), 1 ) ) )
% ( $#saltchars + 1 )
];
}
$salt .= Foswiki::generateRandomChars(8);
}
return Crypt::PasswdMD5::apache_md5_crypt(
Foswiki::encode_utf8($passwd),
Expand All @@ -613,19 +601,7 @@ sub encrypt {
$salt = $this->fetchPass($login) unless $fresh;
if ( $fresh || !$salt ) {
$salt = '$1$';
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
foreach my $i ( 0 .. 7 ) {

# generate a salt not only from rand() but also mixing
# in the users login name: unecessary
$salt .= $saltchars[
(
int( rand( $#saltchars + 1 ) ) +
$i +
ord( substr( $login, $i % length($login), 1 ) ) )
% ( $#saltchars + 1 )
];
}
$salt .= Foswiki::generateRandomChars(8);
}

# crypt is not cross-plaform, so use Crypt::PasswdMD5 if it's available
Expand Down Expand Up @@ -657,19 +633,7 @@ sub encrypt {
my $salt;
$salt = $this->fetchPass($login) unless $fresh;
if ( $fresh || !$salt ) {
my @saltchars = ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' );
foreach my $i ( 0 .. 15 ) {

# generate a salt not only from rand() but also mixing
# in the users login name: unecessary
$salt .= $saltchars[
(
int( rand( $#saltchars + 1 ) ) +
$i +
ord( substr( $login, $i % length($login), 1 ) ) )
% ( $#saltchars + 1 )
];
}
$salt = Foswiki::generateRandomChars(16);
$salt =
Crypt::Eksblowfish::Bcrypt::en_base64(
Foswiki::encode_utf8($salt) );
Expand Down
3 changes: 2 additions & 1 deletion core/lib/Foswiki/Validation.pm
Expand Up @@ -385,7 +385,8 @@ sub _getSecret {
unless ($secret) {

# Use hex encoding to make it cookie-friendly
$secret = Digest::MD5::md5_hex( $cgis->id(), rand(time) );
$secret =
Digest::MD5::md5_hex( $cgis->id(), Foswiki::generateRandomChars(12) );
$cgis->param( _getSecretCookieName(), $secret );
}
return $secret;
Expand Down

0 comments on commit ef15a64

Please sign in to comment.