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

Well a couple days ago I decided to test my extra-newbie perl skills and make a very insecure guestbook. Well any improvements that you would make then don't hesitate to post something on it. It helps alot. Thanks in advance.
#!/usr/bin/perl use CGI; $query = new CGI; print $query->header; print $query->start_html(-title => "Guestbook Thing"); print $query->h1("Guestbook Thing"); &writeit($query); &readit($query); &printit($query); print $query->end_html; sub writeit{ my($query)=@_; print $query->startform; print "Name:"; my $name = $query->param('Name'); my $comments = $query->param('Comments'); print $query->textfield(-name =>'Name'), "<BR>"; print "Message:<BR>", $query->textarea(-name => "Comments",-rows = +> "10",-columns => "50"),"<BR>\n"; print $query->submit(-value => "Submit"); print $query->reset(-value=>Reset); print $query->hr(); print $query->endform; open(WRITE,">>guestbook.txt") || dienice("AHH $!"); print WRITE "$name::$comments\n"; #wonders if he should use th +e join function here... close(WRITE) || dienice("AHH $!"); } sub readit{ open(WRITE,"guestbook.txt") || dienice("AHH $!"); while (<WRITE>){ @_ = @text; } close(WRITE); @done = split(/:/,@text); } sub printit{ print $query->h2('Current Results'); print $query->hr; print "Message By: $done[0]<BR>"; print "&lt;Message&gt;: <Br><blockquote>$done[1]</blockquote>&lt;/ +Message&gt;"; }

Replies are listed 'Best First'.
Re: Test project....
by chromatic (Archbishop) on Nov 06, 2000 at 01:20 UTC
    First, a few words on security:
    • Use -w
    • Use strict
    • Use -T (I don't immediately see anything that'll die under taint mode, but get in the habit of using it)
    • Lock your file (exclusively, when appending and shared, when reading)
    I don't see where @text comes from, and I don't think your readit sub works. Period. You probably don't mean to clobber your $query object by assigning an undefined, springs-into-being array element to it:
    sub readit{ # should use a shared lock here open(READ,"guestbook.txt") || dienice("Couldn't open guestbook for + reading: $!"); local $/; # slurp mode my @text = <READ>; close READ: return split(/:/,@text); }
    That way you don't have to pass in the CGI object (which is a good thing to do in your other subs, and I'm impressed with it), and we can get rid of the global @done this way. Instead, we return the array from this sub, and need to catch it in an array somewhere else.

    This, of course, means printit() will have to change slightly:

    sub printit { my $query = shift; print $query->h2('Current Results'); while (@_) { my $user = shift; my $comment = shift; print "Message by: $user<br>"; print "&lt;Message&gt;<br><blockquote>$comment</blockquote>&lt +;/Message&gt;"; print $query->hr(); } }
    A more detailed version would check $user and $message for containing HTML tags or other nasty potential characters. For now, though, I think you'll do better making it run under -w, strict, and -T.
(Ovid) Re: Test project....
by Ovid (Cardinal) on Nov 06, 2000 at 01:16 UTC
    After looking over your code, I've made a few changes that should help it a bit. I have not changed any of the functionality, but instead have made it a bit more secure and cleaned up some of the code.

    After giving it a bit of thought, I went ahead and converted most of your raw HTML to the CGI equivalent. Ordinarily, I wouldn't do this to anyone else's script, but I noticed you were doing it partway and figured you wouldn't mind my finishing the job, so to speak.

    If you'd like to learn more about CGI scripting, you can check out my online Web programming course. It currently only has two lessons and one appendix, but I'm working on lesson 3, which is a brief overview of CGI scripting security. Hopefully I'll have it up in just a couple of days.

    #!/usr/bin/perl -wT use strict; use CGI; # We use this to prevent someone from inserting HTML tags. # Otherwise, they can include pornographic images, server # side includes, or a meta refresh tags! use HTML::Entities; my @text; # By defining the separator here and not hardcoding it in the script, # we can make it much easier to change in the future! my $separator = "::"; my $query = new CGI; print $query->header, $query->start_html(-title => "Guestbook Thing"), $query->h1("Guestbook Thing"); writeit(); readit(); printit(); print $query->end_html; sub writeit{ my($query)=@_; print $query->startform; print "Name:", $query->textfield( -name => 'Name' ), $query->br(), "Message:<BR>", $query->textarea( -name => "Comments", -rows => "10", -columns => "50" ), $query->br(), $query->submit( -value => "Submit"), $query->reset( -value => "Reset" ), $query->hr(), $query->endform; my $name = $query->param('Name'); my $comments = $query->param('Comments'); # We're going to eliminate newlines so each comment is on one line $comments =~ s/\n/<br>/g; chomp ( $name = encode_entities( $name ) ); chomp ( $comments = encode_entities( $comments ) ); # Oops! We need to get the <br> back! $comments =~ s/&lt;br&gt;/<br>/g; if ( defined $name and defined $comments ) { open(WRITE,">>guestbook.txt") || dienice("AHH $!"); print WRITE ( join $separator, ( $name, $comments ) ) . "\n"; close(WRITE) || dienice("AHH $!"); } } sub readit{ open(WRITE,"guestbook.txt") || dienice("AHH $!"); @text = <WRITE>; chomp @text; close(WRITE); } sub printit{ print $query->h2('Current Results'); foreach ( @text ) { my ( $name, $message ) = split /$separator/, $_; print $query->hr, "Message By: $name", $query->br, "&lt;Message&gt;: ", $query->br, $query->blockquote( $message ), "&lt;/Message&gt;"; } }
    Other ideas:
    • Add timestamps for messages.
    • Add threading (yes, that's quite a project)
    • Give the users proprietary formatting tags.
    • Allow only a subset of HTML. This one is tough, but you can check out this node for a start, if you want to try to work through the regular expression.
    Good luck!

    Cheers,
    Ovid

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

Re: Test project....
by davorg (Chancellor) on Nov 06, 2000 at 14:51 UTC

    One other little addition (or, rather, a subtraction).

    The CGI module has a functional interface which can make writing simple CGI scripts... well... simpler :)

    I don't have time to rewrite the whole script, but here are the first few lines.

    #!/usr/bin/perl use CGI qw(:standard); print header; print start_html(-title => "Guestbook Thing"); print h1("Guestbook Thing"); &writeit(); &readit(); &printit(); print end_html; sub writeit{ print startform; print "Name:"; my $name = param('Name'); my $comments = param('Comments'); print textfield(-name =>'Name'), br; print "Message:", br, textarea(-name => "Comments", -rows => "10", -columns => "50"), br, "\n"; print submit(-value => "Submit"); print reset(-value=>Reset); print hr; print endform; open(WRITE, ">>guestbook.txt") || dienice("AHH $!"); print WRITE "$name::$comments\n"; #wonders if he should use the join + function here... close(WRITE) || dienice("AHH $!"); }
    --
    <http://www.dave.org.uk>

    "Perl makes the fun jobs fun
    and the boring jobs bearable" - me