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

Hi monks,

I would like to be at least certain that my Perl cgi code doesn't fall prey to obvious hack attacks. I'm wondering if I'm on the right right as far cgi security is concerned with the following measures I've taken:

1) taint mode is enabled on the main script

2) fatal_to_browser is turned off

3) use strict is on

4) Perl modules reside in a directory below the web directory (i.e. /usr/home/mysite/perlmodules)

5) Use CGI.pm instead of own code to parse data

6) Every user input goes through the following regex:

# null byte $data =~ s/\x00//g; $data =~ s/"/"/g; $data =~ s/\|/\|/g; # to prevent sql injection $data =~ s/drop/dr0p/ig; $data =~ s/insert/ins3rt/ig; $data =~ s/select/se1ect/ig; # letter 'l' becomes number '1' $data =~ s/delete/de1ete/ig; $data =~ s/alter/a1ter/ig; $data =~ s/update/upd4te/ig; # unix command? $data =~ s/rrm//ig; # javascript $data =~ s/script/skript/ig;
Am I doing the right things? Is there anything terribly obvious that I need to add to the list?

Cheers and many thanks in advance :)

update: I'm using a shared server. I've correctly set the permission to my alloted directory.

Replies are listed 'Best First'.
Re: CGI (in)security
by Tomte (Priest) on Jun 15, 2004 at 12:27 UTC

    points 1 to 3 are sensible, sane and recommended things to do, well done :D

    regarding point 4:
    I'd say move them away to a place outside the actual web-directory like /usr/home/lib/perl/site_perl/5.X.Y/ or somesuch. At least protect the directory against direct access in your webserver configuration.

    regarding point 6:
    To prevent sql-injection and sub-shell-exploits: use prepared-sql-statements with placeholders and untaint cgi-parameters you'll use in system (shell) calls to only allow whats necessary parameter for parameter, not with a generell rule! Using your approach, perfectly normal text I enter might look like I'm trying to be an 31337 h4x0r -- not really a good idea...

    Edit: Updated numbering according to OPs editing

    regards,
    tomte


    An intellectual is someone whose mind watches itself.
    -- Albert Camus

      Thanks tomte!

      I'm using a shared server so I don't have the privilege of moving the Perl modules to the location you suggested.

      As for sql injection prevention, what you said is very true. It's something I'm just learning to do so I'm not quite certain what my options are, hence the reason why I'm taking those 'desperate' measures. But I'll try hard to put your advice to use :)

Re: CGI (in)security
by Happy-the-monk (Canon) on Jun 15, 2004 at 12:27 UTC

    Am I doing the right things?

    Concerning SQL injection there was a discussion recently: Format to save and display.
    I think (and hope to be proven right) that you don't need to mutilate the English words that are SQL keywords if you are careful about semicolons and quote characters.

    Use DBI's quote() method/function to escape your data.

    Cheers, Sören

      DBI will do the quoting for you automatically if you use the bind params format. Something like

      untested and no error checking
      $sql = "select name from customer_table where customer_id = ?"; $sth = $dbh->prepare($sql); $sth->execute($customer_id);
      Execute takes an array of values and subs them in order for the ?'s in the sql statement. It will quote/not quote as nessecary. Should be sufficient to prevent sql injection issues.
Re: CGI (in)security
by tachyon (Chancellor) on Jun 15, 2004 at 13:05 UTC

    Have a look at Re: Sessions, Perl and MySQL for some hints on working with databases. Although people are aware of SQL injection it never ceases to astound me that possibly 50% of all online databases will dump themseleves for you if you query for '%' or 'a%', 'b%', ... 'z%'. If you are going to allow searches using LIKE %<USER_INPUT>% think about the results if you search for wildcards.....

    cheers

    tachyon

      Thanks, tachyon!

      I do in fact intend to provide some kind of searches. Your advice is indeed very helpful :)

Re: CGI (in)security
by bradcathey (Prior) on Jun 15, 2004 at 14:28 UTC
    kiat, where are you keeping your log-on info for your database? Hard-coded into your script? Hidden somewhere? This topic has come up several times here at the monastery. For a recent thread, start here with this one. No black and white outcome, but worth following the thread (and 'no', I'm not touting my own replies, there's other good stuff there :^)

    —Brad
    "Don't ever take a fence down until you know the reason it was put up. " G. K. Chesterton
      Thanks, Brad!

      Yes, I hardcode the database log-on info into the script. I've a single module that does the connection. All my modules reside outside of the web directory (/usr/home/mysite/mymodules). I figured that if anybody gets to the script, he's probably good enough to do anything he desires.

      I followed that node you pointed. I'm trying to understand your password encryption/decryption code. So you're suggesting I should encrypt the log-on info and place it somewhere below the web directory, like /usr/home/mysite/secret?

        The bottom line is that you can only protect yourself from the outside world. If a user has an account on the same shared system as you, that person can read any file that is "available" for the Web because of chmod. So, all things being equal, i could just pop on over to your cgi-bin dir and read your script with a text editor. There is the username, there is the password. Even if you place the username and password in another file in a directory that cannot be accessed by the web, i can still read it, because it has to be chmod'ed appropriately. By encrypting the username and password, i can no longer simply view the file and glean the goods, but, as jayrom pointed out in that thread, i can read the code that decrypts the goods, and therefore i can decrypt the goods myself.

        FWIW, i think it is better to find a group of friends, if possible, and pool in on a hosted box. That way, you have a lot more trust among the users of that system. Someone can still forget to lock the door, though ... oh yeah. A read-only database user is nice too. If you don't need to write to the database, then connect to the database with a user that cannot write to it -- if you have the means to set such an account up, of course. Again, more reasons to get your own box, at least one where you have root access. :)

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)
        
        Yes, kiat, you got my drift. One thing that I did not mention, but was by jeffa, I do chmod that text file that holds the password to 600. But like I said, nothing is fool proof, though jeffa's suggestion about hosting your own is close (but frought with other hassles beyond security).

        —Brad
        "Don't ever take a fence down until you know the reason it was put up. " G. K. Chesterton