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

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

Greetings, fellow monks.

Let's say I have a script that's accessed by the following URL:

http://www.foo.com/cgi-bin/foo.pl?user=foolish&id=2

My main concern is that any user can simply change let's say "user=foolish" to "user=smartone" and just hit the enter key and the script will accept this user as if he is someone else.

I could solve this by making HTTP_REFERER checks, but since some scripts on the system can be accessed from multiple pages, it would be a royal pain in the a**...

Is there anyway to prevent the script from reloading itself without some form input?

Thanks in advance, my friends.

Er Galvão Abbott
a.k.a. Lobo, DaWolf
Webdeveloper

Replies are listed 'Best First'.
Re: Preventing changes on the
by grep (Monsignor) on Feb 18, 2002 at 18:55 UTC
    I commend you on pointing out your 'Security by Obscurity', and you wanting it fixed. The best thing I can recommend is going through Ovid's Web Programming Using Perl course. It handles most of your security concerns as far as programming goes. There are just too many things to point out here in a post on perlmonks. Ovid even covers wog's excellent point (++) that HTTP_REFEFER can be spoofed (along with about everything else).

    Then you have the fun task of locking down (hardening) your box.
    I would recommend looking at:
  • Bastille
  • Security Focus


  • grep
    grep> rm -f /bin/laden
Re: Preventing changes on the
by wog (Curate) on Feb 18, 2002 at 18:55 UTC
    You cannot solve this by making HTTP_REFERER checks. HTTP_REFERER can be spoofed. In fact, almost everything that goes into your script can be spoofed, which is why the answer to your question is "no". The way to solve your problem is by not accepting a user without more authentication.
Re: Preventing changes on the
by dmmiller2k (Chaplain) on Feb 18, 2002 at 19:41 UTC

    Although it is simply adding another layer to your existing Security by Obscurity (and therefore not the entire solution to your problem), you might at least consider accepting only POST-type requests in your script. That is, instead of accessing the script by this hyperlink URL:

    http://www.foo.com/cgi-bin/foo.pl?user=foolish&id=2

    you use a form with all hidden fields and a button (which may be a bitmap):

    <FORM METHOD=POST> <INPUT TYPE=HIDDEN NAME='user' VALUE='foolish'> <INPUT TYPE=HIDDEN NAME='id' VALUE='2'> <!-- use THIS: --> <INPUT TYPE=SUBMIT VALUE='Button Title'> <!-- OR THIS: --> <INPUT TYPE=IMAGE SRC='/images/button_img.jpg' onClick='submit()'> </FORM>

    Update: This won't prevent users from seeing what parameters your script takes (and their values), but rejecting GET requests will at least make it harder to fake them.

    dmm

    If you GIVE a man a fish you feed him for a day
    But,
    TEACH him to fish and you feed him for a lifetime
      POSTs are still quite easy to fake. True, you can't simply edit the URL in the browser address bar, but all you have to do is save the HTML page, edit the value of the hidden field, view the resulting page locally, and hit Submit.

      Further authentication is really a necessity in a scenario like this.

        This is true. I did not intend to suggest that this (and this alone) was a solution to the problem, but it makes it that much more difficult to accomplish (weeding out the "casual" spoofers, as it were). I agree that other security measures are also necessary; for instance, also incorporating HTTP_REFERRER checks and using session cookies, etc.

Re: Preventing changes on the
by Speedy (Monk) on Feb 19, 2002 at 01:30 UTC
    What if you trapped all calls to the page that did not have the proper query string by printing out an error page for each URL that did not match; something like:

    #/usr/bin/perl use CGI qw(:standard); use strict; my $q=new CGI; # Check values from the query string unless ($q->param('user') eq 'foolish' && $q->param('id') eq '2') { print $q->header, $q->start_html(-title=>'Page not found'); print h2("This page was not found"), $q->end_html; exit; } # Real page code follows

    This only works if the script were the first page called from a blank location line by a user. If you use the query string user=foolish&id=2 in a redirect, hidden value, or any other programmatic call to the script, the keys can easily be seen by anyone running the scripts. Then I think you will need one of the more hard-core authentication methods recommended above.

    Live in the moment

      That, of course, won't work if (as appears to be the case) 'user' and 'id' are in fact variables specific to each user.

        You are right. A more complicated scheme would be required for multiple user-id passwords. One method could be to store these user-id pairs in advance in a hash data file, say ../data/user_id, then check the incoming user-id pair against values in the existing hash. For example:

        #/usr/bin/perl use CGI qw(:standard); use GDBM_File; use strict; my $q=new CGI; # Assume an existing saved hash %user_id with 'user' as the key and ' +id' as the value # created earlier by $user_id{"$user"} = $id and stored in ../data/us +er_id my $verify = "../data/user_id"; tie %user_id, 'GDBM_File', $verify, O_RDWR, 0666 or die "Can't tie $ve +rify:$!"; my $user = $q->param('user'); my $id = $q->param('id'); # Check values from the query string against values in hash unless (exists $user_id{"$user"} && $user_id{"$user"} = $id) { print $q->header, $q->start_html(-title=>'Page not found'); print h2("This page was not found"), $q->end_html; exit; } untie %user_id; # Real page code follows
        By now one has other worries, like being sure the hash is locked while a tie is taking place, about how to update and delete values from the hash, about passing a name-password without security, etc.

        Better advice might be to learn about SSL and OS/Web Server authentication for the particular target platform.