Debugging a CGI Script

Victor Odhner plug-discuss@lists.plug.phoenix.az.us
Fri, 10 May 2002 08:55:17 -0700


George Toft wrote:
> Can you expound on this?  My first Perl/CGI book said
> you should always use the -w flag in a CGI script for
> security reasons.

I'd say the program should be explicitly catching
any problems, rather than letting the system do it.

But if getting a server error instead of having the
page displayed enhances security, I guess -w is OK.
Generally the user gets an ugly message that makes
the site look bad.

You can INTERCEPT warnings and direct them to an
error routine that actually displays the error message
in a comprehensible manner, in a well-formed HTML page.
In this case, you can run with #!/bin/perl -w and the
program will fail with a useful (though embarrassing)
message.

See below for details, OR for an example of how Perl
code can indeed be written as a respectable looking
program (there was a side-discussion of this last night
at Sequoia).

Vic

------------------

# By putting this in the BEGIN block, we ensure that any
#   warning message produced by the initial scan of the
#   CGI's code will be displayed via the browser.
#
# Security warning:  You don't want any warning message
#   to go out un-filtered.  The encode_entities function
#   removes the pathnames, leaving only the module name.
#   That way a serendipitous cracker won't have the
#   advantage of knowing where on the system your CGIs
#   reside.

BEGIN {

    $basename = $0;
    $basename =~s{^.*/}{};  # Basename is all after last slash.

    $header = "$basename [$$] ";
    $sent_header   = '';

    use CGI qw/:standard/;

    . . .

    sub encode_entities
    {   # Escape special characters that could make trouble in HTML
output.
        my $msg = shift;
        $msg =~s{\&}{&}g;           # Do this first.
        $msg =~s{\<}{&lt;}g;
        $msg =~s{\>}{&gt;}g;
        $msg =~s{\"}{&quot;}g;
        $msg =~s{--}{==}g;              # Embedded '--' may end HTML
comment.

        $msg =~s{ /\w[-\./\w]*/}{ }g;   # Remove paths from any file
names.
        return $msg;
    }

    sub emit_warning   # This must be defined in or before BEGIN block.
    {   my $txt = shift;
        if ( !$sent_header )  {
                print( "Content-Type: text/html\n\n",
                '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">', "\n",
                "<html>\n<!-- $basename (my_program.pl). -->\n",
                "<head>\n<title>My Nifty CGI Program</title>\n",
                    "</head>\n",
                    "<h1>My Nifty CGI Program</h1>\n"
                );
                $sent_header = 1;
        }
        $txt = &encode_entities( $txt );
        print( '<h2>', $basename, ' -- Web page malfunction:</h2>',
"\n",
                    $txt );
        print( "\n<p><b>NOTE</b>: This problem may <i>not</i> have
been",
               " reported automatically to Customer Support.</p>\n\n",
               "</body>\n\n<html>\n\n" );

        exit( 0 );  # <<< Note, a CGI returns 0 to get its stuff
displayed.
    }

    $SIG{'__WARN__'} = sub { &emit_warning( $_[0] ); };
        # Now any warning message will be neatly displayed on
        #   a correct web page.
    . . .

}   # End of the BEGIN block.

. . . rest of program here . . .