http://qs321.pair.com?node_id=67767

bmhm has asked for the wisdom of the Perl Monks concerning the following question:

I have created a simple password cgi-script for a web page. This script is going to be used for famliy purposes (family photo album) so that not your everyday person can view the pictures. The password will be the same for everyone. Because it is going on the www I want to make sure that it is a secure script. My question(s) is/are:

  1. What are some basic things I can do to make this script more secure?
  2. If need be, would it be wise to use this script on a business web site?
  3. Lastly, is it a wise decision to have the actuall password name in the script or should I call it from a txt file (or something else)?


I did not comment this code because it is self explanatory (or should be anyway). ---

#!usr/bin/local/perl -w use strict; use CGI qw( :standard ); my $p; my $password; $p="howdy"; $password=param('password'); if ($p eq $password) { print "Content-type: text/html\n\n <html><head><title>Password Check</title> </head> <body bgcolor=navy text=white> <h1>It worked</h1> <hr><br> </body></html>"; } else { print "Content-type: text/html\n\n <html><head><title>Password Check</title> </head> <body bgcolor=orange> <h1>Loser -- Try Again</h1> <hr><br> </body></html>"; }


Thanks for the help!

Replies are listed 'Best First'.
(Ovid - why not use the CGI HTML functions?) Re: CGI Password
by Ovid (Cardinal) on Mar 28, 2001 at 16:18 UTC
    Since you are using CGI, you may as well use the CGI HTML generating functions, too.
    #!/usr/bin/perl -wT use strict; use CGI::Pretty qw( :standard ); my ( $p, $password ); $p="howdy"; $password=param('password'); ' Here's where we taint check. $password is undef ' if it doesn't match the regex ( $password ) = ( $password =~ /^(\w+)$/ ); if (defined $password and $p eq $password) { print header, start_html( -title => 'Password Check', -BGCOLOR => 'navy', -text => 'white' ), h1( 'It worked' ), hr(), br(), end_html; } else { print header, start_html( -title => 'Password Check', -BGCOLOR => 'orange' ), h1( 'Loser -- Try Again' ), hr(), br(), end_html; }
    If you enter a valid password, the following is output:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"><head><title>P +assword Check</title> </head><body text="white" bgcolor="navy"> <h1> It worked </h1> <hr><br></body></html>
    Once you get used to them, they are very handy.

    Incidentally, anyone know why <hr> and friends aren't represented as <hr />? I thought those were necessary for valid XHTML (though I know this is transitional, I still thought it was done that way). I am using CGI.pm 2.74.

    You questions were:

    1. What are some basic things I can do to make this script more secure?

      Read perlre. You can also read through <shameless plug>this incomplete CGI course</shameless plug> for more information on security.

    2. If need be, would it be wise to use this script on a business web site?

      No. It's a bad security model. See link in question #1 for more info.

    3. Lastly, is it a wise decision to have the actuall password name in the script or should I call it from a txt file (or something else)?

      Read the link that tinman listed. It looked pretty good (though I just scanned it, so take me with a grain of salt).

    I hope these comments don't seem discouraging. You're asking the right questions :)

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: CGI Password
by fpi (Monk) on Mar 28, 2001 at 12:13 UTC
    I'm not a security expert, but I can suggest a tip which I have used in similar projects such as yours, where security is more of just personal or family privacy as opposed to financial accounts, etc.... Use the crypt function to encrypt your password.

    Basically crypting is one way (there is no uncrypt command), so you need to determine ahead of time the encrypted form of your password, which can be stored in the script or in a text file. Then when your user enters in the password, crypt the entry and compare the crypted version to whatever you have stored.

    The advantage of this is that if someone sees your script or text file where the script was stored, he/she would only see a jumble of letters and numbers and not your password. Makes you feel more secure, because there is no direct easy way to uncrypt it.

    The syntax to determine the crypted form ahead of time is something like this:
    my $p = "howdy"; my $salt = substr($p,0, 2); #define salt however you want my $crypted = crypt($p, $salt); print "Crypted form of $p is $crypted\n";

    Note that $salt is a 2-character variable that determines how $p is crypted. You can define it however you want, even hard code it, but just remember which $salt you use so you can get the same value later. Every perl book which I have read is quite vague about $salt. I suspect someone in PM will post the inner workings of salt and crypt....

    So anyway, after I pre-determine what is the encrypted form of "howdy" (ho8dIXKikSTi2), I would just modify your script as follows:
    my $p="ho8dIXKikSTi2"; #better than displaying actual password my $password=param('password'); my $salt = substr($p,0, 2); my $crypted = crypt($p, $salt); if ($p eq $crypted) { #it worked } else { #try again }
    If you notice, the first 2 letters of the encrypted version is $salt, which in my example is also the first 2 letters of your password. Which, personally, I think is still OK, because a password cracker is not going to know that. Of course, if your password is less than 3 characters, then this might be more of a problem?

    If you eventually want to write your code such that you can change the password online, then store the encrypted password in a separate file.

    I guess this crypting is also good in situations where users create their own passwords, where only the user, and not even the administator, could read the password.

    Hope this helps.
      crypt is showing its age. Brute force cracking is now feasible. The basic idea is good, but I would recommend using Digest::MD5 instead. Add a "secret key" of your choice as a salt before computing the digest.

      UPDATE
      In response to arhuman, that is why I said to add a secret key as a salt. That can be of any length, and its purpose is to increase the searchspace so that brute force fails.

        Hehe, MD5 is aging too, it can now be cracked in hours (up to 6 char) or in few days for a longer password (8 char).

        Proof here.

        Try Digest::SHA1...

        UPDATE :
        In response to tilly
        IMHO SHA1 is a better choice beccause SHA1 seems more secure than MD5 (resists better to collision attack) and is SLOWER
        which is this case is an advantage as it renders brute force attack less effective
        (the time penalty is unoticeable for checking/creating ONE password, but is a real problem when you check thousands or more...)

        "Only Bad Coders Badly Code In Perl" (OBC2IP)
Re: CGI Password
by bjelli (Pilgrim) on Mar 28, 2001 at 13:47 UTC
Re: CGI Password
by tinman (Curate) on Mar 28, 2001 at 12:39 UTC

    I'm definitely no security expert myself.. but a suggestion in addition to using crypt might be..

    to use SSL to encrypt the pasword transfer from browser to webserver.. although not essential for a home/family setup, I can't imagine most business related sites not offering that option..HTTP sends everything in plain text, so anyone with a sniffer can simply lift your password off the wire, as it were....

    this is one of the places to start, if you want to see a real implementation, a bit dated, but still holds true for lots of sites, I think.. also read "A guide to web authentication alternatives", given in the references section...
    HTH

        A quick perusal of the wayback machine shows the new home of A Guide to Web Authentication Alternatives by Jan Wolter. Do please note the dates - this document was written in 1997 and last revised in 2003. While it does give a useful grounding in some of the technologies, don't expect it to reflect the state of the art.

Re: CGI Password
by Anonymous Monk on Mar 28, 2001 at 11:52 UTC
    What are some basic things I can do to make this script more secure?

    Run perl with the -T switch to turn taint checking on.

    If need be, would it be wise to use this script on a business web site?

    I'm not sure I understand what you mean. If your business needs this, then yes, if it doesn't, then no.

    Lastly, is it a wise decision to have the actuall password name in the script or should I call it from a txt file (or something else)?

    First, you shouldn't store the plaintext password, store it crypt'd. As for where to store it, that depends on your system's setup, you'll preferably want to store it in a file which can be read by as few people as possible, that could be your CGI if you're using suEXEC or similar mechanism.
Re: CGI Password
by rbi (Monk) on Mar 28, 2001 at 22:22 UTC
    I've found very useful and flexible the user_manage script by Lincoln Stein.
    Maybe you can get some ideas, or see how a secure authentication can be
    This script lets you manage your users, and you can then point the .htaccess file of your protected directory to its generated password and group files.
    Also, you can let users change their password using that script (it works both from the prompt and from the browser).

    Hope this helps.
    Ciao,
    Roberto