http://www.perlmonks.org?node_id=257331

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

Hi Monks,

I'm building a little program that will allow my client to update her own "Tip of The Week" page, on an otherwise static site.

The "back-end" is a text file, and the script dumps the contents when the script is called pristinely. Passing parameters allows the page content to be updated.

The page is only meant to be accessed by a signed in user. I created a simple little log-in screen, but its not even close to providing real security.

This is to run in a shared unix hosting environment to which I have only ftp access.

I am thinking about coming up with some kind of temporary, date-based key which could be passed via cgi and generated at auth time. I know I'm just playing with matches, though.

Thanks for reading.

Here's what I wrote:

#!/usr/bin/perl # authtip2.pl - authenticate to change the tip of the week. use CGI; my $pwfile = "pw.dat"; my @begindate = split (/ /, scalar localtime); my ($dow,$mon,$day,$hh_mm_ss,$tz,$yyyy) = @begindate; my $query = CGI->new(); my $status = $query->param('status'); my $user = $query->param('user'); my $pass = $query->param('pass'); my $hidden = $query->param('hide'); my %auth = ($user => $pass); $myself = $query->url; if (defined $user) { &TEST($user); } &HEADER; if (defined $status) { print $query->h3( "$status $hidden" ); } &SIGN_IN_FORM; sub HEADER { my $status = shift; print $query->header( "text/html" ), $query->start_html(-title => "Tip Authenticator", -style=>{'src'=>'../mystyle.css'}), $query->h1( "Tip Changer Sign-In $hidden" ); } sub SIGN_IN_FORM { $myself = $query->url; print ' <form method="get" action=', $myself, ' name=""> <p> <font face="Elephant"> <input type="text" name="user"> user name</font></p> <p> <font face="Elephant"> <input type="password" name="pass"> password</font></p> <p> <font face="Elephant"> <input type="hidden" name="hidden" value="logged_in"> <input type="submit" name="Submit" value="Submit"> </font> </form> '; } sub TEST { open (PW, "pw.dat") or die "$! Couldn't open pw file"; while (<PW>) { chomp; my @record = split /:/; next unless $record[0] eq $user; $auth{$user} eq $record[1] ? return (&validuser) : return(print $query->redirect("$myself?status=Incorrect Passwo +rd")) ; } print $query->redirect("$myself?status=Bad User Name"); } sub validuser { print $query->redirect("http://localhost/tip2.pl?status=$key"); }

 

I don't need fort knox here, but I don't want to create a massive vulnerability. Is there a module I should be reading up on?

The other part script displays and updates the Tip of the Week.

#!/usr/local/bin/perl -w # Tip of The Week editor, generator use strict; use CGI; $| = 1; my $query = CGI->new(); my $selfurl = $query->self_url; open (TIP, "tip.txt") or die "$! Sorry, contact admin"; my @input = $query->param(); my $tipdata = $query->param('formdata'); my $publish = $tipdata; $tipdata = <TIP> unless (defined $tipdata); close (TIP); &header; &init_javascript; unless (@input) { &Display_page_for_enduser; } if ($publish) { open (TIP, ">tip.txt") or die; print TIP "$publish"; close (TIP); } print $query->h2($tipdata); &showtiptext($tipdata); &print_table; &publish($tipdata); sub header { print $query->header( "text/html" ), $query->start_html(-title => "Tip Editor", -style=>{'src'=>'smc_style.css'}) ,$query->h1("Tip Editor") } sub showtiptext { my $myself = shift; print $query->start_form(-method=>"post", -action=>"$selfurl" ); print $query->textarea(-name=>'formdata', -default=>"$myself", -rows=>5, -columns=>50), $query->br; print $query->submit(-name=>'submit', -value=>'submit'); print $query->endform; } sub print_table { print ' <table width="368" border="0" cellspacing="1" align="center" height="1 +74" cellpadding="4" background="/images/sunset3.jpg"> <tr valign="top"> <td width="372" height="119" class="text"> <p><img aLIGN=left src="/pics/robinSmile.jpg" width="93" height= +"92"> <b>Robin Says: '; print $query->p, $query->b("$tipdata"), $query->br; print '</b></td></tr></table> <div align="center"> <a href="javascript:closeWindow()" onMouseOut="MM_swapImgRestore()" onMouseOver="MM_swapImage("closeWindow","","slices/closeWindow_o.gi +f",1);" > <img name="closeWindow" src="../slices/closeWindow.gif" width="109" height="15" border="0"> </a> </div> '; } sub publish { my $myself = shift; print $query->start_form(-method=>"post", -action=>"$selfurl" ); print $query->hidden( -name => "filebait" , -value => "$myself"); print $query->submit(-name=>'publish', -value=>'publish'); print $query->endform; } sub Display_page_for_enduser { &print_table; die; }

Thanks for reading.

edited: Mon May 12 14:29:15 2003 by jeffa - readmore tags

Replies are listed 'Best First'.
Re: Beginner CGI programming, authentication
by Zaxo (Archbishop) on May 12, 2003 at 03:19 UTC

    I recommend using the server's authentication methods. See the Apache manual under Auth modules.

    After Compline,
    Zaxo

Re: Beginner CGI programming, authentication
by DigitalKitty (Parson) on May 12, 2003 at 07:21 UTC
    Hi mkahn.

    I agree with Zaxo. There is nothing wrong with implementing your own authentication tool but the risk of a design
•Re: Beginner CGI programming, authentication
by merlyn (Sage) on May 12, 2003 at 10:25 UTC
    I'm in the "keep it simple, stupid" camp. I'd have the normal URL invoke a CGI script to show the data, like
    /path/to/the/message
    but when your friend wants to edit it, adds ?mode=edit to the end of the URL:
    /path/to/the/message?mode=edit
    which invokes the same program, but the param is detected, and now a form comes out with two fields: the original text in a textarea, and a place for a password. When this form is submitted, it'll be returned to the same CGI program, which can check the password against a crypt() password included in the program, and if a match, updates the file and shows the new result. Code looks something like this:
    use CGI qw(:all); my $MESSAGE_LOCATION = "/path/to/file.txt"; my $CRYPTED_PASSWORD = "aaPwJ9XL9Y99E"; ## this is from print crypt("hello", "aa"), so "hello" is the password print header, start_html; if (my $new_text = param('text') and my $password = param('password')) + { ## update mode... is the password good? if (crypt($password, $CRYPTED_PASSWORD) eq $CRYPTED_PASSWORD) { open F, ">$MESSAGE_LOCATION" or die; print F $new_text; close F; print h1('updated!'), p('The message was updated!'); } } elsif (param('mode') eq 'edit') { ## secret mode open F, $MESSAGE_LOCATION or die; my $message = join '', <F>; close F; print h1('edit the message'); print start_form; print textarea('text', $message); print 'password: ', password_field('password'); print submit('update'); print end_form; } ## always display the result: open F, $MESSAGE_LOCATION or die; my $message = join '', <F>; close F; print h1('Welcome to our info page'); print h2("Today's top tip:"), escapeHTML($message); print end_html;
    There. Whipped out quickly, so it might not fully work, but you can flesh out the details I hope.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Thanks for the responses, randal and katie.

      This works fine, but the permission on the text file needs to be set to 777 to work in a browser.

      HTAccess is probably a more accepted way of doing this, but I like avoiding the pop-up.

        Nothing should ever be "777".

        It's true that the userid of the webserver needs to be able to read and write the message file, but there are (at least) two ways to accomplish that:

        • Use the chown command to make the file owned by that user.
        • chmod the file to 666 (not 777)
        You never want to have a world writable file also be an executable. That's just begging for someone to come along and put random content into it and then executing that.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.