Chapter 20. Security in Dynamic Content

 

“Some people are heroes. And some people jot down notes. Sometimes they're the same person.”

 
 --The Truth—Terry Pratchett

The Common Gateway Interface (CGI) is an integral part of most Web sites today. Dynamic content allows a site owner to gain a better understanding of what visitors to the site are looking for, and it allows site users to communicate better with the owner of the site.

Unfortunately, CGI, and other types of dynamic content, also open up many more security holes than a site with plain HTML. This leaves a server administrator with a quandary: How do you protect the server and still allow site owners to interact with their site visitors?

CGI scripts are always going to present an inherent security risk. Any file that is executable, or interacts with files that are executable has risks associated with it. There are ways to minimize these risks.

By following good programming practices, strict file permission settings, and carefully monitoring your server you can severely diminish the potential for attacks.

There are two types of security concerns associated with CGI and dynamic content. The first is the potential to exploit the script or program to either break into your server, or take advantage of the resources on your server, such as your mail daemon. The second security risk is that a badly written program will use up an inordinate amount of system resources (RAM, Disk Space, or CPU). This can slow down your server, or worse, cause it to become completely unresponsive and crash.

This chapter focuses on both types of security risks, and gives you some pointers about how to lessen the risks and make your server run smoother. Most of the suggestions in this chapter will work whether you are running a single site on a server or multiple sites on a shared server.

Understanding Security Risks in Dynamic Content

Most server administrators do not take security risks seriously until something happens to their server. After all, there are literally millions of servers on the Internet, what are the chances that someone would attack yours?

Believe it or not the chances are pretty good. The Code Red worm was the first wake up call for many server administrators. In early August 2001 messages started appearing in hosting message boards all over the Internet asking what the following requests were:

"GET /default.ida?NNNNNNNNNNNNNNNNNNNNN"

They were, of course, part of the Code Red worm, which was spreading through the Internet very quickly and compromising many servers in the process. Those servers in turn infected other servers.

You are probably thinking to yourself, “That's fine, I am running Apache, Code Red only affected Windows servers running IIS. So it wouldn't have affected me.”

That's not entirely true. You have to pay for bandwidth, especially if you collocate your server. Which means that the thousands of extra hits caused by a security hole within a dynamic content framework—Microsoft's Index Server—might have cost you money.

If you are still not sure, let's use an example that might be on your server. If you have a Web site, you probably have a contact feedback form, or perhaps several, that customers can use to send you messages.

Contact feedback forms are a great convenience to both you and your customers. You can direct traffic where you want it to go, and you can ensure the message has the information you need in it.

Feedback forms and other form processing scripts are so popular that there are Perl libraries written just to parse the data.

If your form processing script is written in Perl, it probably has code similar to this:

#!/usr/bin/perl
require 'cgi-lib.pl';

print &PrintHeader;

$subject = "FORM Data Received";

$mailprog = "/usr/sbin/sendmail";

&ReadParse;


$recipient = $FORM($recipient);

open(MAIL, "|$mailprog -t");
print MAIL "To: $recipient
";
print MAIL "Subject: $subject
";

foreach $key (keys(%FORM)) {
        print MAIL "$key = $FORM{ $key} 
";
}
close(MAIL);

print <<EOF;

<html>
<body>
<h2>Your form has been sucessfully submitted</h2>
</body>
</html>
EOF
;

This script uses the cgi-lib Perl library (http://cgi-lib.berkeley.edu). cgi-lib, and other Perl libraries, are written to make some of the more repetitive Perl tasks simpler. Rather than type 10 lines of code to process the form data, a simple call to cgi-lib.pl provides a programmer with the same results.

The recipient of the e-mail is specified in a hidden field in the form. This means that anyone with a little knowledge of how CGI works can use your form, and server, to mass e-mail other users. To make matters worse you would have no idea because it would not get logged in Sendmail, or in the server records (except you would notice a lot more hits to that file).

As you can see, security has to be a concern for everyone who uses dynamic content.

Compiled Versus Parsed Dynamic Content

CGI Scripts fall into two different categories: Compiled and Parsed. A compiled script is a script that is able to run by itself without the need of an interpreter. Compiled scripts are similar to Windows executable files, double-click it and it runs.

A parsed script is a script that requires an interpreter to run. Perl scripts are great examples of parsed scripts. At the top of every Perl script is a line that identifies the Perl interpreter. It usually looks something like this:

#!/usr/bin/perl

Shell scripts are another example of parsed scripts.

Some programmers will tell you that, for security reasons, you should always use compiled scripts instead of parsed scripts. The truth is that parsed scripts can be as secure as compiled scripts.

The reasons people feel compiled scripts are safer than parsed scripts are twofold. The first has to do with the nature of compiled scripts. You can compile a script in binary format, which prevents potential intruders from obtaining the source code. If they cannot get the source code, it is harder for them to find weaknesses in the script.

The second reason some prefer compiled scripts is that it is more difficult to make system calls in compiled scripts. For example, if you are writing a shell script that outputs domain information, it is very simple to install a “whois” binary and create a system call to that command and output the information. This is a lot more difficult to do with a compiled script, so if you are writing a “whois” CGI Script in C, you will probably program the entire sequence from scratch, or even more likely, from borrowed code.

Of course, there is nothing wrong with using borrowed code, or current libraries. Many C/C++, Perl, and PHP Libraries perform routine, laborious tasks, and save time for programmers. Generally, these libraries have been thoroughly tested and are secure. But, never trust others to do your security checks. No matter how often a piece of code, or library has been used, it never hurts to review it for security holes.

Although these are compelling reasons to think that compiled scripts are safer than parsed scripts, the truth of the matter is that most modern parsed scripts provide programmers with many of the same security capabilities that compiled scripts have.

So, with good server security and good programming practices, a parsed script can be as secure as a compiled script.

Writing Safer Dynamic Content

There are many programming languages that are used to create CGI Scripts. When it comes to Apache, the three most popular languages, by far, are Perl, PHP, and C/C++.

To cgi-bin or Not

One of the most common questions asked by server administrators is whether or not they should put CGI Scripts in a cgi-bin directory.

The answer is that there is no inherent advantage to putting scripts in a specific directory. The same potential for security risks exists whether the scripts are in one place or spread over the server.

That being said, the advantage you get by having CGI scripts located in one place is that it is easier for you, as the server administrator, to monitor them for potential problems. It is also easier to maintain uniform permission standards across all directories and sites.

Common Mistakes

Although there are specific issues with each language, there are also some common guidelines, which should be followed by all CGI programmers.

Make sure you validate all input. Never accept user information without verifying it. This includes watching out for buffer overflows. A buffer overflow occurs when a fixed length is set for an input variable, and data is sent that is greater than that variable length. The excess characters can be used to execute a system command or cause other problems. Most modern languages, such as Perl, JAVA, and PHP have safeguards against buffer overflows. Unfortunately, neither C nor C++ have these safeguards.

As mentioned previously, limit system calls as much as possible. Only call to system programs when necessary, and as with other variables, validate input to ensure the calls are behaving as you expected them to.

If there is an error with a script, avoid providing too much information regarding the error. Follow the example set by Apache—if a user requests a document that is not available, Apache returns the 404 Error, and simply states that the file is not found. The simpler your error messages, the less likely someone will be to use that information to exploit your server.

In general, it is a good idea to avoid giving too much information out about the server configuration.

Perl

Perl is the programming language most closely associated with CGI. It's flexibility and power has made it popular with Web developers for years.

The downside to this popularity is that literally hundreds of thousands of inexperienced programmers write or edit Perl scripts that are available for download. These scripts are a great convenience, but are often riddled with security holes similar to the example used in the beginning of this chapter.

There are several steps you can take to secure Perl scripts. Some of these steps will apply to other parsed languages, whereas some are Perl specific.

Always run Perl with the -T and -w tags enabled. The -T tag tells Perl to run in taint mode. Taint mode assumes that all variables received from the command line, STDIN, or the users environment variables are unsafe, and will not pass these variables to anything outside of your program.

If you enable taint mode you will, at a minimum, need to do to things to run your scripts. First you will need to set your path:

ENV{ 'PATH'}  = '/bin:/usr/bin:/usr/sbin';

Or whatever directories you want to ensure are included in your path.

You will also need to untaint any variables, that are going to pass data to another program. A common example of this is taint checking an e-mail address before passing it to Sendmail. You can untaint an e-mail address like this:

$email =~ /(w{ 1} [w-.]*)@([w-.]+)/ or die "not a valid e-mail address";
$email = "$1@$2";

These lines parse the variable $email to make sure the e-mail address contains valid character constructs. Note, this is not the same as ensuring the e-mail address is valid. If the address appears to be okay, the variable information is removed and replaced by $1 and $2. Although this is simply a copy of the original variable, it is now clean and can be passed onto other programs.

Any Perl variable that will be passed to another program should pass through this type of taint check. Of course, if your variable is already tainted, your results will become tainted.

In addition to the taint flag, Perl scripts should have the -w flag enabled. The -w flag tells Perl to run in warn mode. This means that Perl will warn you about potential problems or obsolete commands within a script.

The taint and warn flags will increase the security of your scripts, but you should also follow secure programming practices.

Again referring to the script at the beginning of this chapter, one way it can be secured is by specifying which referrers are allowed to access the script:

if (($ENV{ 'HTTP_REFERER'} ) &&
($ENV{ 'HTTP_REFERER'}  !~ /^http://test.example.com/)) {
print "Content-type: text/html

";
print "Sorry, you are not able to access this script
";
exit 0;
}

These lines check the referring page. If it is not within the test.example.com domain an error message is printed and the program is exited. You can be even more specific and set the referral to certain pages (such as, test.example.com/form.html).

This security precaution should not be used alone. The HTTP_REFERRER field can be easily faked. It is, however, part of a good programming strategy.

You can also use environmental variables to limit GET requests:

if($ENV{ 'REQUEST_METHOD'}  eq "GET"){
print "Content-type: text/html

";
print "Sorry, you are not able to access this script
";
exit 0;
}

Finally, you can also limit the e-mail addresses that are allowed to use a form:

This creates an array that includes my wife and my e-mail addresses. This array can be used to limit the valid e-mail addresses to which the script can send information.

For more information about securing Perl scripts, you might want to read Sams Teach Yourself Perl in 24 Hours, Second Edition.

PHP

PHP is a relatively new scripting language, but its ease of use, cross-platform capabilities, and Web integration has quickly made it a favorite among Web developers.

There are two ways in which PHP scripts can be run: As part of an Apache module or as CGI scripts.

For security reasons, you are better off running PHP as an Apache module. When run as an Apache module, PHP derives security settings from Apache, which is already secure.

When you run PHP as an Apache module, the PHP environment settings are taken from the PHP configuration file called php.ini. By securing your php.ini file you are able to diminish the security risks posed by PHP scripts.

One of the easiest changes you can make is to enable the safe_mode variable in the php.ini file. Because of additional security concerns, when you enable safe_mode you should do it in conjunction with run suEXEC (discussed later in this chapter).

When safe_mode is enabled PHP compares the owner of a file that is requested by a script with the owner of the script. For instance, if you were to put this script on your server:

<?php
 readfile('/etc/passwd)';
?>

Anyone who accessed it would be able to read the contents of your passwd file. Enabling safe_mode will prevent that script from being successfully executed, and an error will be generated.

In conjunction with safe_mode, you should also limit PHP script access to the Apache root document directory. You can do this by enabling the doc_root variable. The doc_root variable defines the location of the Web server root, so users cannot use PHP scripts to access server files outside of that realm. The variable is simple to set, simply point it to the root Web directory:

doc_root = /home/httpd/

Finally, you can also limit the types of system calls that a user can make by using the disable_functions variable. Disable_functions operates independently of safe_mode and is a comma delimitated list of function names:

disable_functions = fwrite();

C and C++

As Perl and PHP have grown in popularity C and C++ have diminished as CGI scripting tools. However, there are still many C and C++ CGI scripts used on the Web today, and it is important to understand what type of security risks are involved.

The biggest security concern with C and C++ is that they are very susceptible to buffer overflows, as described earlier.

You can prevent buffer overflows from occurring by dynamically setting the buffer size. The World Wide Web Consortium recommends the following in their Security FAQ:

char* read_POST() {
   int query_size=atoi(getenv("CONTENT_LENGTH"));
   char* query_string = (char*) malloc(query_size);
   if (query_string != NULL)
      fread(query_string,query_size,1,stdin);
   return query_string;
}

This reads the data from a form, and assigns the sets the size of the incoming data to the size of the CGI environment variable CONTENT_LENGTH. In other words, it sets the size of the buffer to the size of the incoming data.

In addition to dynamically setting the buffer size, you should use standard library functions that defend against buffer overflows. For example, instead of using the library calls strcpy and strcat, use strncpy and strncat.

As with Perl and PHP when programming in C and C++ it is important to check incoming data before passing it to a shell command.

Even though C and C++ scripts are compiled they can still cause the same level of damage if you do not perform the same data integrity checks as you would with a Perl or PHP script.

In fact, a C/C++ script can often cause more damage because there is no controlled space, also called a programming sandbox, where experimental code can be tried out to see if there are any potential problems.

Wrappers

A wrapper is program that runs on your server and helps lessen the security risks associated with CGI scripts. There are several ways this can be done.

The most common method of security control is to allow CGI scripts to be run under the user ID of the site owner—the owner of the directories within which the site resides.

How does allowing scripts to be executed with the same ID as the site owner increase security? In a non-wrapper environment the scripts are executed as the Apache user. This means that the Apache user has to be a member of the same group as the site owner. It also means that anyone on the server has the ability to execute a script, or write to a database in any other site directory on the server.

The two most common wrappers used with Apache are CGIWrap and suEXEC.

CGIWrap

CGIWrap is a very popular wrapper. In addition to the advantages of wrappers already discussed, CGIWrap also performs security checks on the script.

CGIWrap is not installed natively with either Apache or operating systems, so you will need to download and install it. The source code is available from the Unix Tools Web site (http://www.unixtools.org). As of this writing the latest version is 3.7.1.

The following instructions are for installation on RedHat 7.1. Please check the CGIWrap installation file for variations of this process for different operating systems.

Start by uncompressing the file:

[root@test /root]# tar xvfz cgiwrap-3.7.1.tar.gz

Change into the newly created directory:

[root@test /root]# cd cgiwrap-3.7.1/

At this point you can either begin the installation process with the program defaults, or you can compile with specific program tags enabled.

You can view the options available by typing:

[root@test cgiwrap-3.7.1]# ./configure —help

At a minimum you will need to specify the Apache user, and the directory in which you would like to install CGIWrap. An example of a CGIWrap configuration might look something like this:

 [root@test cgiwrap-3.7.1]# ./configure —with-httpd-user=nobody 
> —with-install-dir=/home/httpd/cgi-bin/ 
> —[email protected]

Finally, you will need to compile and install the program:

[root@test cgiwrap-3.7.1]# make && make install

You have now successfully installed CGIWrap; the next step is to setup Apache to use CGIWrap. The most common method for doing this is to use the URL Rewriting Engine.

URL Rewriting Engine

The URL Rewriting Engine, or mod_rewrite, is a standard Apache module that allows you to rewrite URL requests on the fly.

Apache processes HTTP requests in phases. The Apache API allows modules to write hooks into these different phases.

Mod_rewrite intercepts an HTTP request in two of these phases. The first place is after the request for a URL has been made, which is the URL to filename translation. The second is when the specific directory configurations are processed.

So between the time Apache determines which server, or virtual server, to send the traffic to and the request is returned to the browser, is when mod_rewrite is activated.

A detailed discussion of mod_rewrite is outside the realm of this chapter, for more information, please visit the URL Rewriting Engine Web site (http://httpd.apache.org/docs/mod/mod_rewrite.html).

Mod_rewrite and CGIWrap

So, how do mod_rewrite and CGIWrap work together? It is actually pretty simple. Let's start by creating a virtual host:

<VirtualHost 192.168.0.40>
DocumentRoot /home/httpd/foo/public_html
ServerName test.example.com
</VirtualHost>

Add the ScriptAlias directive:

ScriptAlias /cgi-bin/ /home/httpd/foo/public_html/cgi-bin/

Turn on URL Rewriting for this host:

RewriteEngine On

Finally, we need to redirect traffic for /cgi-bin to the CGIWrap directory we created earlier:

RewriteRule ^/~(.*)/cgi-bin/(.*) /home/httpd/cgi-bin/$1/$2 [PT]

Put everything together and it should look something like this:

<VirtualHost 192.168.0.40>
DocumentRoot /home/httpd/foo/public_html
ServerName test.example.com
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
RewriteEngine On
RewriteRule ^/~(.*)/cgi-bin/(.*) /home/httpd/cgi-bin/$1/$2 [PT]
</VirtualHost>

Of course, make sure that you have enabled the mod_rewrite module so it is loaded, and make sure you have enabled ExecCGI for the directory.

Of course you will want to test to see if your new configuration is working. The simplest way to do this is to make a simple Perl script and link to it:

#!/usr/bin/perl -Tw
print "Content-type: text/html

";
print "Hello, world!
";

When you click the link to the script you should see a message in the Status Window telling you that you are being redirected (it will probably go by pretty quickly, so don't blink). If you do, congratulations, you have managed to successfully install CGIWrap.

How does this help secure your dynamic content? The cgi-bin has been removed from the root Web directory to another directory to which the site user does not have access. This allows you, as the server administrator, to maintain better control over the scripts that are being used on the server. You can either review code that is submitted by users, or restrict access to only those scripts you have preinstalled.

It also allows you to severely limit the allowed directives within the user's home directory.

suEXEC

Apache includes it's own security wrapper, called suEXEC. As with CGIWrap, suEXEC allows a server administrator to configure sites so that CGI scripts run as the owner of the site, as opposed to the owner of the httpd process.

SuEXEC can also provide performance gains over CGIWrap. Because suEXEC is compiled into the Apache module, it responds faster and resides in memory. It does not have to be called every time a script is executed.

The way suEXEC works is relatively simple. When a request for a CGI or Server-Side Includes file that does not have the same user ID as the Apache Web server is made Apache passes the information to the suEXEC wrapper, which performs a series of checks on the script. If the script passes the checks it is executed. If it does not, an error is created and logged, and a 500 status code is returned.

There are 20 suEXEC checks that a script has to pass before Apache will execute it. You can read more about the specific checks at the Apache suEXEC Web site (http://httpd.apache.org/docs/suexec.html).

Before you can configure suEXEC you have to make sure it is installed and running. When you compiled your Apache installation you should have done so using the —enable-suexec flag when you installed Apache.

If suEXEC has been successfully compiled into your Apache installation you should see a line appear in your httpd_error log when Apache starts, that looks like this:

[Fri Nov  2 08:35:40 2001] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)

The most common way to use suEXEC is with the User and Group directives. When you create a virtual host you can assign a user to that virtual host. That user will now be able to execute scripts with his or her username.

Let's use the same virtual host we set up in the last section to demonstrate this. Change the virtual host configuration to look something like this:

<VirtualHost 192.168.0.40>
DocumentRoot /home/httpd/foo/public_html
ServerName test.example.com
ScriptAlias /cgi-bin/ /home/httpd/foo/public_html/cgi-bin/
User allan
Group allan
</VirtualHost>

User and group have to be defined in order for suEXEC to work. If only one or the other is defined suEXEC will fail the process and an error will be generated.

A couple of other things you need to worry about with suEXEC. The owner of the Web server process needs to be able to su to the user and group of the script owner. If the Apache user cannot then the script will fail.

Using the hello script created in the last section, set permissions to the user group you defined in the virtual host configuration, and try to execute the script.

To see what an error looks like, change the owner of the script so it does not match what is defined in the virtual host configuration.

You might have to adjust some settings to get suEXEC to work initially; it tends to be more complicated than CGIWrap to configure. However, the additional security it provides, is worth the initial configuration problems.

Checking Code in Existing Scripts

It is not uncommon to install third party CGI scripts on your server. In fact there are hundreds of sites that provide CGI scripts at either a low cost or completely free. Of course, after reading this chapter you are undoubtedly worried about the security measures in those scripts.

The more worried you are the better. Although it is not necessary to never use free scripts, it is important to thoroughly examine anything you install on your server for potential security breaches.

Of course it does help to know what to look for. Reading this chapter has, hopefully, given you some idea of the more common security holes. No matter what language the script is written in, there are several commonalities you can look for to see if it is secure, or, if it is not, if you can fix it to make it more secure.

The biggest security hole to watch out for is scripts that read or write to the system. It's probably been mentioned a dozen times in this chapter, but it bears repeating. Never write unchecked data to your server, and don't allow a script to read from any file on the server.

Look for calls to system programs. Scripts that use Sendmail are the best examples of this. There is nothing inherently wrong with calling system programs, but always make sure the data being passed to those programs is checked.

Hard code system paths into the script. Some programmers prefer to use the PATH CGI environment variable as a shortcut, but that can create security problems.

Someone looking to hack into your site can alter the PATH environment variable to point to the program he wants to execute, as opposed to your program.

If you watch for these three problems and correct them if you do see them, you can save yourself a lot of headaches in the future.

Special Issues with Windows CGI

The security issues discussed so far in this chapter: monitor input, minimize information shared about the server, and avoid system calls all apply to Windows scripting as well. However, there are some special considerations when programming CGI scripts on the Windows platform.

One thing to watch for is that Windows likes to create executable files. This is especially true with FrontPage Extensions. If these executables are created in a directory that has executable permissions, such as a cgi-bin directory, then everyone has access to it. One way to avoid this is problem to set permissions for CGI folders to allow scripts to be run, but not allow files to be executed.

Another common security error for parsed scripts is to put the interpreter in the Web server path. When installing Perl or PHP, the executable file should go into a path that the scripts can reach, but that is outside of the Web server root path so an intruder cannot access it.

You can use suEXEC with a Microsoft Windows installation of Apache. The configuration works in the same manner, taking into account the different directory structure. CGIWrap is not currently available for Microsoft Windows installations. Finally, as with other operating systems, be careful with your directory permissions and script permissions.

Summary

CGI scripts and other forms of dynamic content are an integral part of most Web sites.

Although the functionality that dynamic content provides is important to enhance the user experience, it comes with some security risk. The more interaction you create with your users the greater security risk you create for your server.

There are four rules that you can follow to help increase your server's level of security, while still providing the interaction with your site that users want:

  • Always validate input. Whether it is from a form, database queries, or any other way of getting data, never accept it blindly.

  • Whenever possible use libraries that have been time-tested for security. In addition to shortening your programming cycle the authors of the code have, hopefully, repeatedly tested the code for security. Of course, no matter how secure a library is, it is still important to review it to ensure there are no potential security holes that might have been overlooked.

  • It is especially important to validate anything that is going to be sent to a system program. Never pass data to another program without first checking it.

  • Do not trust the PATH CGI Variable Environment. Always use hard-coded paths.

If you follow these steps, the security of your server will be greatly enhanced.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset