[Image] Allowing scripts to run on your server can create large security holes. If the script receives information other than what you were expecting, a malicious user can at least read and/or destroy CGI-created data and at worst take the first (most difficult) step in gaining total access to the server. The two biggest security holes are file i/o (reading from and writing to files) and sub-shells (where variables in the program can be interpreted as commands). The general way to avoid security holes is the same: Don't trust anything the user sends back! (and also for you administrators out there, never ever run a web server as root) ---------------------------------------------------------------------------- What to look out for * File I/O File i/o means reading from and writing to files on the server. If file i/o is done improperly, the web-client can read or destroy files other than the ones you intended them to. The Perl commands related to file i/o (roughly in their relative level of danger) are: o open (">", "<", ">>" also see the sub-shell discussion below for "|") o unlink/rmdir o read (also the construct) o dbmopen (and the use of the associative array created) o print/printf/sprintf o write o close/dbmclose --------------- * Sub-shells One of the great abilities of Unix is to run other programs in the middle of one of your programs. This can be quite dangerous if you don't look out. Ways of spawning sub-shells in Perl include: (these are pretty much equally dangerous) o eval o system o exec o `shell_command ` (backquotes) o open(HANDLE,"shell_ command|"); or open(HANDLE,"|shell_command"); ---------------------------------------------------------------------------- How to make scripts more secure * General Rule: Make the user-returned information fit your expectations Just because you have a form to access your CGI script doesn't mean that your script will always be called from that script; especially if someone's trying to use your script to do something bad. Don't do anything with an arbitrary set of variables o Make sure you know the specific variables your program uses. Don't do things like this: @user_args=split(/&/,$ENV{'QUERY_STRING'}); foreach $file (@user_args) { open(FILE,">$file"); print FILE "Hi there\n"; close(FILE); } o Make the contents of your variables look like you expect them to + Use s/// and tr// liberally! Whenever possible, do something like this: read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'}); @pairs=split(/&/,$buff); foreach $pair (@pairs) { ($name,$value)=split(/=/,$pair); $value=~tr/+/ /; $value=~s/%([0-9A-Fa-f]{2})/pack("C",hex($1))/eg; ############# the next line is the important one ############### $value=~tr/A-Za-z0-9//cd; #this wipes out anything non-alphanumeric $form{$name}=$value; } + Be even more specific if you can (like tr/0-9//cd; if you only want digits). If you do the alphanumeric-only tr, you don't need the 2 standard translation lines. ----------------------------------------------------------------------- * Specific Guidelines for File I/O If your script has any file i/o, you want to make sure that any file description has no ~s or ../s in it, since those characters could be used to create or read from unexpected files. Do things like this: ## user-input in associative array %form $form{'filename'}=~tr/~//d; #get rid of ~s $form{'filename'}=~s/\.\.\///g; #get rid of ../s open(HANDLE,"$startpath/$form{'filename'}"); --------------- * Specific Guidelines for Sub-shells If your script spawns any sub-shells, you need to make sure it contains no shell metacharacters from user-input. Shell metacharacters are special characters used to separate, customize, or relate Unix shell commands. Shell metacharacters include (I doubt this is a comprehensive list): { } [ ] | ; < > & ( ) ! \ Any user-supplied information which will be sent to a sub-shell should be run through something like this: $form{'search'}=~tr/A-Za-z0-9 ._=+-//cd; ## $form{'search'} is now clean but can still contain the characters we specify open(FIND,"grep $form{'search'} /export/home/me/mydatabase |"); You can also do something a little less invasive than that by escaping those bad characters with a line like this: $form{'search'}=~s/([{}[]|\;<>()])/\\$1/g; This will put an escaping back-slash in front of any potentially dangerous character. ---------------------------------------------------------------------------- That's enough to give you some idea of what issues are involved and how to make your scripts more secure. Check out other CGI & WWW security pages at Yahoo. Back to the CGI Security Compilation