January 17, 2021
Hot Topics:

Security Issues in Perl Scripts

  • By Jordan Dimov
  • Send Email »
  • More Articles »


In Perl, yet another way to read the output of an external program is to enclose the command in backticks. So if we wanted to store the contents of our stats file in the scalar $stats, we could do something like:

     $stats = `cat /usr/stats/$username`;

This does go through the shell. Any script that involves user input inside of a pair of backticks is at risk to all the security problems that we discussed earlier.

There are a couple of different ways to try to make the shell not interpret possible meta-characters, but the safest thing to do is to not use backticks. Instead, open a pipe to STDIN, then fork and execute the external program like we did at the end of the previous section with open().

The eval() and the /e regex modifier

The eval() function can execute a block of Perl code at runtime, returning the value of the last evaluated statement. This kind of functionality is often used for things such as configuration files, which can be written as perl code. Unless you absolutely trust the source of code to be passed to eval(), do not do things like eval $userinput. This also applies to the /e modifier in regular expressions that makes Perl interpret the expression before processing it.

Filtering User Input

One common approach to solving most of the problems we've been discussing in this section is to filter out unwanted meta-characters and other problematic data. For example, we could filter out all periods to avoid backwards directory traversal. Similarly, we can fail whenever we see invalid characters.

This strategy is called "black-listing". The philosophy is that if something is not explicitly forbidden, then it must be okay. A better strategy is "white-listing", which states that if something is not explicitly allowed, then it must be forbidden.

The most significant problem with a black-list is that it's very hard to keep it complete and updated. You may forget to filter out a certain character, or your program may have to switch to a different shell with different set of meta-characters.

Instead of filtering out unwanted meta-characters and other dangerous input, filter in only the input that is legitimate. The following snippet for example will cease to execute a security critical operation if the user input contains anything except letters, numbers, a dot, or an @ sign (characters that may be found in a user's email address):

     unless ($useraddress =~ /^([-\@\w.]+)$/) {       print "Security error.\n";       exit (1);     }

The basic idea is not to try to compile a list of special values to guard against but rather to come up with a list of values that are safe to accept. The choice of acceptable input values will, of course, vary from one application to another. Acceptable values should be chosen in such a way as to minimize their damage-causing potential.

Avoiding the shell

Of course, you should also strive to avoid shells as much as possible. However, this technique is more broadly applicable. If you call an editor, which has special sequences, you can make sure those sequences are not permissible.

Often, you can avoid using external programs to perform a function by using an existing perl module. The Comprehensive Perl Archive Network (CPAN -- www.cpan.org) is a huge resource of tested functional modules for almost anything that a standard UNIX toolset can do. While it may take a little more work to include a module and call it instead of calling an external program, the modular approach is in general far more secure and often a lot more flexible. Just to illustrate the point, using Net::SMTP instead of exec()'ing sendmail --T'' can save you the trouble of going through the shell and can prevent your users from exploiting known vulnerabilities in the 'sendmail' agent.

Other sources of security problems

Insecure Environmental Variables

User input is indeed the chief source of security problems with Perl programs, but there are other factors that should be considered when writing secure Perl code. One commonly exploited weakness of scripts running under the shell or by a web server are insecure environmental variables, most commonly the PATH variable. When you access an external application or utility from within your code by only specifying a relative path to it, you put at odds the security of your whole program and the system that it's running on. Say you have a system() call like this:

  system ("txt2html", "/usr/stats/jdimov");

For this call to work, you assume that the txt2html file is in a directory that is contained somewhere in the PATH variable. But should it happen so that an attacker alters your path to point to some other malicious program with the same name, your system's security is no more guaranteed.

In order to prevent things like this from happening, every program that needs to be even remotely security conscious should start with something like:

     #!/usr/bin/perl -wT     require 5.001;     use strict;     $ENV{PATH} = join ':' => split (" ", << '__EOPATH__');       /usr/bin       /bin       /maybe/something/else     __EOPATH__

If the program relies on other environmental variables, they should also be explicitly redefined before being used.

Another dangerous variable (this one is more Perl-specific) is the @INC array variable which is a lot like PATH except it specifies where Perl should look for modules to be included in the program. The problem with @INC is pretty much the same as that of PATH someone might point your Perl to a module that has the same name and does about the same thing as the module you expect, but it also does something subversive in the background. Therefore, @INC should not be trusted any more than PATH and should be completely redefined before including any external modules.

Page 2 of 3

This article was originally published on February 6, 2001

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date