This section demonstrates how a small input submitted by a malicious user may hog the whole server.

Imagine an online service that allows users to create a canvas on the server side and do some fancy image processing. Among the inputs that are to be submitted by the user are the width and the height of the canvas. If the program doesn't restrict the maximum values for them, some smart user may ask your program to create a canvas of 1,000,000 × 1,000,000 pixels. In addition to working the CPU rather heavily, the processes that serve this request will probably eat all the available memory (including the swap space) and kill the server.

How can the user do this, if you have prepared a form with a pull-down list of possible choices? Simply by saving the form and later editing it, or by using a GET request. Don't forget that what you receive is merely an input from a user agent, and it can very easily be spoofed by anyone knowing how to use LWP::UserAgent or something equivalent. There are various techniques to prevent users from fiddling with forms, but it's much simpler to make your code check that the submitted values are acceptable and then move on.

If you do some relational database processing, you will often encounter the need to read lots of records from the database and then print them to the browser after they are formatted. Let's look at an example.

We will use DBI and for this example. Assume that we are already connected to the database server (refer to the DBI manpage for a complete reference to the DBI module):

my $q = new CGI;
my $default_hits = 10;
my $hits = int $q->param("hints") || $default_hits;

my $do_sql = "SELECT from foo LIMIT 0,$hits";
my $sth = $dbh->prepare($do_sql);

while (@row_ary = $sth->fetchrow_array) {
    # do DB accumulation into some variable
# print the data

In this example, the records are accumulated in the program data before they are printed. The variables that are used to store the records that matched the query will grow by the size of the data, in turn causing the httpd process to grow by the same amount.

Imagine a search engine interface that allows a user to choose to display 10, 50, or 100 results. What happens if the user modifies the form to ask for 1,000,000 hits? If you have a big enough database, and if you rely on the fact that the only valid choices would be 10, 50, or 100 without actually checking, your database engine may unexpectedly return a million records. Your process will grow by many megabytes, possibly eating all the available memory and swap space.

The obvious solution is to disallow arbitrary inputs for critical variables like this one. Another improvement is to avoid the accumulation of matched records in the program data. Instead, you could use DBI::bind_columns( ) or a similar function to print each record as it is fetched from the database. In Chapter 20 we will talk about this technique in depth.