CGI.pm and Use of uninitialized value in pattern match

A

A. Farber

Hello Perl users,

I have a habit of untainting input data this way in my
CGI scripts (example: http://pastebin.com/m46057a70):

$user = $1 if $query->param('user') =~ /(\w{3,12})/;
$pass = $1 if $query->param('pass') =~ /(\w{8})/;
......
unless ($user and $pass and ...) {
print $query->start_form(), ...
} else {
# do the real work with untainted data
}

and wonder, how to get rid of the warnings
"Use of uninitialized value in pattern match (m//)"
without (ab)using the no warnings qw(uninitialized)?

Thank you
Alex
 
P

Peter Makholm

A. Farber said:
I have a habit of untainting input data this way in my
CGI scripts (example: http://pastebin.com/m46057a70):

$user = $1 if $query->param('user') =~ /(\w{3,12})/;
$pass = $1 if $query->param('pass') =~ /(\w{8})/;

I wouldn't call

{
no warnings qw(uninitialized);

$user = $1 if $query->param('user') =~ /(\w{3,12})/;
$pass = $1 if $query->param('pass') =~ /(\w{8})/;
}

abuse of the functionality. It clearly shows that you are aware of the
posibility of the parameters to be uninitialized and explicitly
decided that it doesn't matter.

If you wont accept that solution you have to test for definedness in
some way:

$user = $1 if defined( $query->param('user') )
&& $query->param('user') =~ /(\w{3,12})/;

$user = $1 if ($query->param('user') // '') =~ /(\w{3,12})/;

$user = $1 if
(defined $query->param('user') ? $query->param('user') : '') =~ /(\w{3,12})/;


I wouldn't rule out the 'no warnings' solution. But most of the time I
would use the first of the above three without really considering any
alternatives.

//Makholm
 
T

Tad J McClellan

A. Farber said:
Hello Perl users,

I have a habit of untainting input data this way in my
CGI scripts (example: http://pastebin.com/m46057a70):

$user = $1 if $query->param('user') =~ /(\w{3,12})/;


If a 20-character username is entered, you want to silently
truncate it to 12 chars?

Validation should match against the *entire input*.

Put some anchors in there.

and wonder, how to get rid of the warnings
"Use of uninitialized value in pattern match (m//)"
without (ab)using the no warnings qw(uninitialized)?


You currently have a value of undef to indicate invalid data.

Use some other false value, such as the empty string.

Using m// in list context, and adding anchors:

my($user) = $query->param('user') =~ /^(\w{3,12})$/ || '';
 
T

Tad J McClellan

$pass = $1 if $query->param('pass') =~ /(\w{8})/;


You don't even _allow_ punctuation characters in passwords?

Good security generally *requires* punctuation characters in passwords.
 
A

A. Farber

{
    no warnings qw(uninitialized);

    $user = $1 if $query->param('user') =~ /(\w{3,12})/;
    $pass = $1 if $query->param('pass') =~ /(\w{8})/;

}

This looks better to me, thanks
 
A

A. Farber

Hello Tad,

You don't even _allow_ punctuation characters in passwords?

Good security generally *requires* punctuation characters in passwords.

your previous comment about anchors etc. has been good, thanks.

No, I don't want punctuation :) Here in Germany
the keyboard layout is different from the english.
And the latter layout pop-ups from time to time on
different occasions, but I don't want the users
to call me in those cases (work as sysadmin here).

Here is my current password-generating function
(I also removed chars like 8 and B, because some
users really manage to mix them up...):

use constant POSSIBLE =>
'abcdefghijkmnpqrstuvwxACDEFGHJKMNPQRSTUVWX234579';

# generate a random password (also used for 2-chars salt)
sub genpass($) {
my ($len, $pass);

$len = shift;
$pass = '';

$pass .= substr(POSSIBLE, (int(rand(length(POSSIBLE)))), 1)
while length($pass) < $len;

# make sure the password contains 2 digit
# and 2 letters for the Windows policy
return genpass($len) if $len >=8 &&
($pass =~ tr/0-9// < 2 ||
$pass =~ tr/a-z// < 2 ||
$pass =~ tr/A-Z// < 2);

return $pass;
}


It generates strings like:

C3Uns7te
gRc4mVA4
5sF4DC5m
sx5N4eSm
WV7e3bTJ
3UbfM7g5
ni4TrqS3
5NArf3Pb

I wish there were some clever module
for better-to-memorize passwords though.
Maybe by alternate vowels and consonants?

Regards
Alex
 
R

Randal L. Schwartz

A> return genpass($len) if $len >=8 &&
A> ($pass =~ tr/0-9// < 2 ||
A> $pass =~ tr/a-z// < 2 ||
A> $pass =~ tr/A-Z// < 2);

Apparently, you believe that Perl has tail-recursion elimination. It doesn't.
You should probably have written this as a loop instead. Something like:

{
$pass = [your calculation here];
return $pass if $len < 8; # short passwords skip checks
# if any quality check fails, try again:
redo if $pass =~ tr/0-9// < 2;
redo if $pass =~ tr/a-z// < 2;
redo if $pass =~ tr/A-Z// < 2;
return $pass; # passed all checks, return it
}

print "Just another Perl hacker,"; # the original
 
G

Gunnar Hjalmarsson

A. Farber said:
I have a habit of untainting input data this way in my
CGI scripts (example: http://pastebin.com/m46057a70):

$user = $1 if $query->param('user') =~ /(\w{3,12})/;
$pass = $1 if $query->param('pass') =~ /(\w{8})/;
.....
unless ($user and $pass and ...) {
print $query->start_form(), ...
} else {
# do the real work with untainted data
}

and wonder, how to get rid of the warnings
"Use of uninitialized value in pattern match (m//)"
without (ab)using the no warnings qw(uninitialized)?

If the form include input controls named "user" respective "pass", those
parameters are always defined whether the fields were filled or not.
Consequently I don't understand the problem in the first place.
 
U

Uri Guttman

GH> If the form include input controls named "user" respective "pass",
GH> those parameters are always defined whether the fields were filled or
GH> not. Consequently I don't understand the problem in the first place.

you can always fake a form submission with LWP that doesn't set all the
fields and that could trigger the uninit warning. web apps should always
do full server side validation and never assume good data is coming in
(especially even when using javascript to validate stuff).

uri
 
G

Gunnar Hjalmarsson

Uri said:
GH> If the form include input controls named "user" respective "pass",
GH> those parameters are always defined whether the fields were filled or
GH> not. Consequently I don't understand the problem in the first place.

you can always fake a form submission with LWP that doesn't set all the
fields and that could trigger the uninit warning.

Indeed, but if Alex doesn't like such direct access to his script, those
warnings might be a good thing (or not...).
web apps should always
do full server side validation and never assume good data is coming in
(especially even when using javascript to validate stuff).

Absolutely. It was not my intention to give any other impression.
 
A

A. Farber

Apparently, you believe that Perl has tail-recursion elimination.  It doesn't.
You should probably have written this as a loop instead.  Something like:

  {
     $pass = [your calculation here];
     return $pass if $len < 8; # short passwords skip checks
     # if any quality check fails, try again:
     redo if $pass =~ tr/0-9// < 2;
     redo if $pass =~ tr/a-z// < 2;
     redo if $pass =~ tr/A-Z// < 2;
     return $pass; # passed all checks, return it
  }

Thank you Randal, this looks good! Don't know why I wanted recursion

Gunnar, yes I just always check and untaint every input
and don't assume my script will be called from my web-form only.

Greetings from Germany
Alex
 
A

A. Farber

And also when the script is called for
the 1st time, then those params aren't defined
 
G

Gunnar Hjalmarsson

A. Farber said:
I ... don't assume my script will be called from my web-form only.

Me neither. Of course. Just thought you might be interested in getting a
hint when someone posts to it from somewhere else.
And also when the script is called for the 1st time, then those params
aren't defined

Aha, so the form is generated by the same script. Now even I understand. :)
 
P

Peter J. Holzer

I wish there were some clever module
for better-to-memorize passwords though.
Maybe by alternate vowels and consonants?

Not a perl module, but might help: The "pwgen" utility present in some
Linux distributions.

hp
 
H

Hans Mulder

Peter said:
Not a perl module, but might help: The "pwgen" utility present in some
Linux distributions.

The pwgen utility on my Linux box does not have an option to
alternate vowels and consonants.

How about this:

#!/usr/bin/perl -w

use strict;
use warnings;

my @vowels = split //, 'aeiou';
my @consonants = split //, 'bcdfghjklmnpqrstvwxyz';

foreach (1..4) {
print $consonants[rand @consonants];
print $vowels[rand @vowels];
}

print "\n";

Hope this helps,

-- HansM
 
R

Randal L. Schwartz

A> I wish there were some clever module
A> for better-to-memorize passwords though.

You mean besides Crypt::YAPassGen, and the five modules it references in its
manpage? Which I found in the CPAN with a 5 second search?

Have people forgotten how to search the cpan? It's Perl's greatest resource!

print "Just another Perl hacker,"; # the original
 
A

A. Farber

Hello,

A> I wish there were some clever module
A> for better-to-memorize passwords though.

You mean besides Crypt::YAPassGen, and the
five modules it references in its manpage?
 Which I found in the CPAN with a 5 second search?

unfortunately doesn't work for me, maybe because it's version 0.02?

afarber@ablsw01:~> ~/yapassgen.pl
File is not a perl storable at ../../lib/Storable.pm (autosplit
into ../../lib/auto/Storable/_retrieve.al) line 331, at /home/afarber/
perl/lib/perl5/site_perl/5.8.8/Crypt/YAPassGen.pm line 91

afarber@ablsw01:~> cat ~/yapassgen.pl
#!/usr/bin/perl -w

use Crypt::YAPassGen;

my $passgen = Crypt::YAPassGen->new(
freq => '/usr/share/dict/linux.words',
length => 10,
post_subs => [sub { $_ = uc }, "digits"],
);

my $passwd = $passgen->generate();

afarber@ablsw01:~> cat /etc/*release
CentOS release 5.2 (Final)

(^^^this is compatible to RHEL 5.2)

afarber@ablsw01:~> perl -v

This is perl, v5.8.8 built for i386-linux-thread-multi
.....

afarber@ablsw01:~> tail /usr/share/dict/linux.words
Zythia
zythum
Zyzomys
Zyzzogeton
zyzzyva
zyzzyvas
ZZ
Zz
zZt
ZZZ

Regards
Alex
 
A

A. Farber

alternate vowels and consonants.

         #!/usr/bin/perl -w

         use strict;
         use warnings;

         my @vowels = split //, 'aeiou';
         my @consonants = split //, 'bcdfghjklmnpqrstvwxyz';

         foreach (1..4) {
             print $consonants[rand @consonants];
             print $vowels[rand @vowels];
         }

         print "\n";

This is good, thank you Hans.

Since Windows wants to have digits in passwords,
I've decided that 13490 are more like vowels
and 25678 are consonants :) and added them:

#!/usr/bin/perl -w

use strict;
use warnings;

# XXX Better get rid of 0OlI18B etc. below, that user mix up
my @vowels = split //, 'AEIOUaeiou13490';
my @consonants = split //,
'BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz25678';

sub genpass($) {
my ($len, $pass);
$len = shift;

do {
$pass = '';
do {
$pass .= $consonants[rand @consonants];
$pass .= $vowels[rand @vowels];
} while (length $pass < $len);

# escape here when generating 2-chars salt
last if $len < 8;

# make sure the password contains 2 digit and 2 letters
} while ($pass =~ tr/0-9// < 2 ||
$pass =~ tr/a-z// < 2 ||
$pass =~ tr/A-Z// < 2);

return $pass;
}

print genpass(8), "\n";
 
P

Peter J. Holzer

The pwgen utility on my Linux box does not have an option to
alternate vowels and consonants.

NAME
pwgen - generate pronounceable passwords

It does this by default (well, actually it uses a somewhat more
sophisticated algorithm). It does have an option to turn it off:

-s, --secure
Generate completely random, hard-to-memorize passwords.

This is getting a bit off-topic, though.

hp
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top