How to Write Secure Perl Scripts

Perl is the main server scripting language for web servers. It is used because of its ease of use and its power, but because of the ease of use, a power perl can be exploited. This document will tell you how to secure your web server from attacks.

Perl scripts can be identified by the outside world as they normally have .pl or .cgi extensions.
Tainting
IF you have ever seen a perl script, you will have noticed that on the first line there is the path to the perl program. eg:
#!/usr/local/bin/perl -w
The -w tells perl to give warnings if there are errors in the code. The security equivalent is to disallow insecure commands from user input. The way this works is that user input cannot execute a command, but any variable created in the script can. eg;
$input= ; STDIN is from the user, so it is considered as insecure
$untainted = "hi";
To enable this secure mode (or tainting, to call it its proper name) add -t to the line, eg; #!/usr/local/bin/perl -T or #!/usr/local/bin/perl -Tw
You can untaint a variable by putting the data it contains into a variable name $number, e.g. $input = $1; The following perl script parses user input if it doesn’t contain dangerous characters tells perl it is secure:

Warning: Enabling tainting doesn’t prevent a user from reading a file. For example, if you run a script that reads a file, someone could attempt to open ../../../../../../etc/passwd (the Unix password file)
use strict;
This command tells perl that from now on all sub’s and all variables must be declared before using them. This secures your script but also makes it harder to write. You can disable use strictly at any time with the command
no strict;
Setuid
Setuid is a Unix command that allows a script to have rights above that of whoever is running it. This is only useful if you want the script to have more access than the default user that runs the script without running the script as someone with high access.
To suid, a script, login to your server through telnet, change to the folder with the script (use the ls and cd commands) then type the command:
chmod u+s scriptname.pl
There is a bug on many Unix systems that makes perl crash when you try to run a suid script. You can get around this through ‘c wrappers’. These are just programs written in c then execute the perl script. An example C script: (compile with gcc cwrapper.c -o cwrap.cgi)

Checking the user

You can restrict access of web pages by the IP of the computer (so you could make it so only local computers or only aol users can access), by the url that redirected to the perl script or by requiring a password.
In Perl $HTTP_REFERRER is the page that sent the browser to the page and REMOTE_ADDR is the IP.
Checking for these through if-then statements. eg;

The above script checks for a password as input and that the computer connecting has the IP 127.0.0.1
Warning: The IP can be faked, and the referrer is even easier to fake. Check groovyweb.uklinux.net soon for the web page I’m writing that can fake the referrer. Normally the perl script will require an IP the same as the server, so it is just a matter of going to dos, typing ping www.server.com, getting the IP then faking the referrer. The referrer can be faked as follows: (or use a program that does it for you if you are lazy)

Telnet to the server where you want to download the file from (start>run>telnet), eg.
on port 80 (as usual) then issue the get command to retrieve the file you want as usual eg:
get /env_check.htm
But then rather than pressing enter twice, instead press enter, then type:
Referrer: http://madeup.com/fake/referer.html
Then press enter.
The IP if harder to fake, but still insecure. The password option is quite a common method, but if someone reads your script they will discover the password.
To stop this, the perl encryption function can be used

The crypt() function

This is perls built-in encryption function. An example is:
$try=crypt(“pop”, aa);
crypt() is a function. The first bit is what is to be encrypted. The second is what it is to be encrypted against. This type of encryption is quite secure as it works by you’re script requiring the user to input a password. The input is then encrypted using aa as the key. The results are then checked against the result of encrypting “pop” with aa. This is how Unix user passwords work.
The problem is, a program can be written which tries thousands of different words to see if they are the answer. To give you an idea of how to secure your perl script, included is a very buggy perl script (I’m very new to perl, and therefore quite crap at the programming side of it).
I wrote it a couple of days ago when I was looking around the code of a message board system I, and found that the admin password (which the administrator set up) was always encrypted against ‘aa’. This means all people using the message board system could be cracked using the same program. If the administrator changed aa to something else, or if there was another script that used the perl crypt() function you could just change the line in the program below that says the password is encrypted against ‘aa’.
I tested the below program and it seems to work, e-mail me with problems (ie. it turns out it does completely the wrong thing or something).

The longer the password and the words it is encrypted against the longer the time to crack. An 8 against 8 passwords is very hard to crack.

This is the main threat and has been already covered, but I’ll go over it again more clearly. If you have a perl script which allows the user to input anything, they could input a; which tells perl to execute the next stuff as a Unix command.
So the line system(“grep $userinput datafile.txt > file.txt”); with “cd / ; rm -rf * ; echo” becomes grep ; cd / ; rm -rf * ; echo datafile.txt > file.txt . As said above, remove special characters &;`'”|*?~<>^()[]{}$nr and execute the commands as a list, eg system(“grep”,$userinput,”datafile.txt”,”> file.txt”);

Many well known scripts contain this hole. phf and Matt wrights Formmailer (most e-mail forms use this) were the most well known. Excite and Hotmail were hacked through simliar methods.

Why you shouldn’t use hidden form data

If you have in your html page to tell your perl script who the user is you are allowing anyone to fake their id. Url’s with form data is in the form script.pl?userid=12, so anyone can just guess userid 1 will exist and change the url to ?userid=1. Use crypt()ed form data.