Chapter 16. Secure Web Development

As a Linux user, you'll eventually dabble in Web development. That's a given. And Linux offers copious tools and opportunities in this area. However, when you're writing your own Web tools, you must ensure that you don't inadvertently open security holes on your otherwise secure host. This chapter will quickly examine secure Web development techniques.

Development Risk Factors: A Wide Overview

On every Web development project, you'll face three chief risks that are manifested in logical sequence, from your project's bare beginnings to its ultimate completion:

  • Faulty tools—You must keep up with the times and obtain the latest tools. Languages and libraries are carefully scrutinized but security issues within them surface periodically. If your tools are flawed, even your best efforts will fail.

  • Flawed code—Even if you have flawless tools, you must know how to properly use them. Some programming languages enforce strict guidelines, while others don't (C as opposed to Perl, for example). But most employ only cursory security checks on your code—if they do any at all. That means that you, and not the compiler or interpreter, are ultimately responsible for ensuring that your code enhances system security (or at worst, does not impede or degrade it).

  • Environment—Even if you use flawless tools and employ them properly, unexpected contingencies can arise. The environment is a good example. Attackers or even coworkers can either maliciously or unwittingly alter the environment and alter your program's execution and performance.

The best advice, therefore, is to choose one language, learn it well, and stay current on all security issues relevant to it. Beyond that, this chapter covers some common programming errors, means of avoiding them, and tools to help you in that regard.

Spawning Shells

Several functions in C, C++, and Perl spawn shells or otherwise execute programs insecurely:

  • system()

  • popen()

  • open()

  • eval

  • exec

Wherever possible, you should avoid these functions. The following sections illustrate why.

Executing Shell Commands with system()

Here are two very risky practices:

  • Constructing internal command lines using user input.

  • Executing shell commands from within C or Perl.

Programmers often perform these tasks using the system() function.

system() in C

system() is available via the standard library (stdlib.h) and provides a mechanism to execute a shell command from a C or C++ program. As explained on the system (3) man page:

  • system() executes a command specified in string by calling /bin/sh -c string, and returns after the command has been completed.

Do not use system() in

  • Publicly accessible programs or scripts on your Web host

  • SGID programs or scripts

  • SUID programs or scripts

Here's why: Attackers can execute shell commands riding on the system() function, either by manipulating environment variables or pushing metacharacters or additional commands onto the argument list. In particular, you should always avoid giving attackers an opportunity to pass metacharacters to any function that calls a shell.

Table 16.1 lists commonly used metacharacters in various shells (bash, csh, ksh).

Table 16.1. Various Shell Metacharacters in bash, csh, and ksh

Purposebashcshksh
Append output to a file>>>>>>
Append STDERRand STDOUT>>&>& 
Command separator;;;
Command substitution'…'''…'
Execute in background&&&
Group commands( )( )( )
History substitution![job #]%[job #] 
Home directory symbol/~/~~
Literal (but not $or /)"…""…""…"
Literal quote'…''…''…'
Logical AND&&&&&&
Logical OR||||||
Match multiple characters***
Match a single character???
Match multiple characters[…][…][…]
Path break symbol///
Pipe|||
Redirect input to a line<<<<<<
Redirect input<<>
Redirect output>>>
Redirect STDERRand STDOUT2>>& 
Variable substitution${…}$${…}

To appreciate the danger of using system(), consider this C++ code, which allows a user to execute a shell command:

int main() {
     char usercommand[20];
    cout << "Please enter a command:";
    cin >> usercommand;
    cout << "You entered" << usercommand << "
";
    system(usercommand);
}

No one would actually write such a program, but it's useful for demonstration purposes. The code grabs a user command and executes it:

$testsystem
Please enter a command: ls
total 456
-rwxrwxrwx    1 9053     9000         530 Jun  9  1995 Makefile
-rwxrwxrwx    1 9053     9000        2799 Jun 14  1995 README
-rwxrwxrwx    1 9053     9000        1001 Jun  9  1995 arp.c
-rwxrwxrwx    1 9053     9000        6988 Jun  9  1995 dnit.c
-rwxrwxrwx    1 9053     9000        1047 May 13  1995 dnit.h
-rwxrwxrwx    1 9053     9000           0 Jun  9  1995 errlist
-rwxrwxrwx    1 9053     9000        1621 Jun  9  1995 ether.c
-rwxrwxrwx    1 mikal    user        6798 Jun 22 07:11 ipspoof.c

This doesn't seem threatening. But suppose that the user entered a different command instead:

$testsystem
Please enter a command: ls;finger
total 456
-rwxrwxrwx    1 9053     9000         530 Jun  9  1995 Makefile
-rwxrwxrwx    1 9053     9000        2799 Jun 14  1995 README
-rwxrwxrwx    1 9053     9000        1001 Jun  9  1995 arp.c
-rwxrwxrwx    1 9053     9000        6988 Jun  9  1995 dnit.c
-rwxrwxrwx    1 9053     9000        1047 May 13  1995 dnit.h
-rwxrwxrwx    1 9053     9000           0 Jun  9  1995 errlist
-rwxrwxrwx    1 9053     9000        1621 Jun  9  1995 ether.c
-rwxrwxrwx    1 mikal    user        6798 Jun 22 07:11 ipspoof.c
Login    Name                  TTY Idle When       Office
root     Big Bad-Ass           q0       Thu 15:15
mikal    Chief Developer       *ftp      Thu 22:37  Room 200

The code allows users to execute additional commands by adding the command separator metacharacter (;). True, attackers are restricted to appending commands without whitespace (they cannot successfully execute ls;command1argument;command2argument, for example), but nevertheless, this opens a serious hole.

system() can be attacked in other ways, too. On some systems, local attackers can alter the Input Field Separator shell variable to break up paths in your system() function into separate commands. For example, suppose you did this:

system("/bin/mydate");

If the attacker can reset the IFS variable to " ", the shell will now parse your system call like this:

bin date

This will run a program named bin in the current directory.

system() in Perl

In Perl, system() is even more dangerous. Consider a program that performs a function identical to the preceding C++ example:

#!/bin/perl
print "Please enter a command:";
$command=<STDIN>;
system($command);

Here, Perl slurps up multiple additional commands, whether separated by whitespace or not:

$testsystem.pl
Please enter a command: ls –l;cat /etc/passwd
total 8
-rw-r--r--    1 root     sys            0 Jun 25 00:26 perltest.txt
-rwxr-xr-x    1 root     sys          102 Jun 25 00:25 testsystem.pl
root:s1rwxYeA1tqjM:0:0:Big Bad-Ass:/:/bin/csh
shutdown:*:0:0:shutdown,,,,,,:/shutdown:/bin/csh
sysadm:*:0:0:System V Administration:/usr/admin:/bin/sh
diag:*:0:996:Hardware Diagnostics:/usr/diags:/bin/csh
daemon:*:1:1:daemons:/:/dev/null
bin:*:2:2:System Tools Owner:/bin:/dev/null
uucp:*:3:5:UUCP Owner:/usr/lib/uucp:/bin/csh
sys:*:4:0:System Activity Owner:/var/adm:/bin/sh
adm:*:5:3:Accounting Files Owner:/var/adm:/bin/sh
lp:WCI1iUWKqUqDM:9:9:Print Spooler Owner:/var/spool/lp:/bin/sh
nuucp:*:10:10:Remote UUCP User:/var/spool/uucppublic:/usr/lib/uucp/uucico
auditor:*:11:0:Audit Activity Owner:/auditor:/bin/sh
dbadmin:*:12:0:Security Database Owner:/dbadmin:/bin/sh
rfindd:WCI1iUWKqUqDM:66:1:Rfind Daemon and Fsdump:/var/rfindd:/bin/sh
EZsetup:*:992:998:System Setup,,,,,,,:/var/sysadmdesktop/EZsetup:/bin/csh
demos:*:993:997:Demonstration User:/usr/demos:/bin/csh
OutOfBox:*:995:997:Out of Box Experience,,,,,,,:/usr/people/tour:/bin/csh
guest:WCI1iUWKqUqDM:998:998:Guest Account:/usr/people/guest:/bin/csh
4Dgifts:*:999:998:4Dgifts Account,,,,,,,:/usr/people/4Dgifts:/bin/csh
nobody:*:60001:60001:SVR4 nobody uid:/dev/null:/dev/null
noaccess:*:60002:60002:uid no access:/dev/null:/dev/null
nobody:*:-2:-2:original nobody uid:/dev/null:/dev/null
mikal:RFkVtMV5Aj0o6:1110:20:Michael:/usr/people/mikal:/bin/csh
hapless:UhmpfxFtbBGeI:1117:20:Hapless Linux User: /usr/people/hapless:/bin/csh

Hence, you should never build a command line with user input for handling by system().

Caution

This is true even if you think you've found a solution to control what gets read into STDIN. For example, some Webmasters present the user with check boxes, radio lists, or other read-only clickable elements that have predefined values. This isn't safe either. Nothing prevents a cracker from downloading the HTML source, altering the predefined values, and submitting the form. However, if you insist on doing things this way, at least verify form content:

if($var{'option 1'} ne "opt1" || $var{'option 2'} ne "opt2") {
        print "You entered an illegal field value
";
        exit;
        }
    }

popen() in C and C++

popen() is available via the standard I/O library (stdio.h) and provides a mechanism to execute a shell command from a C or C++ program. As explained on the popen (3) man page:

  • The popen function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; The resulting stream is correspondingly read-only or write-only. The command argument is a pointer to a null-terminated string containing a shell command line. This command is passed to /bin/sh using the -c flag; Interpretation, if any, is performed by the shell.

Do not use popen() in

  • Publicly accessible programs or scripts on your Web host

  • SGID programs or scripts

  • SUID programs or scripts

popen() invites various attacks, the most serious of which is the use of metacharacters to trick popen() into invoking alternate commands. This problem crops up more often than you'd think, even in professionally developed applications. For example, in October 1998, the RSI Advise team reported an IRIX vulnerability to BUGTRAQ about autofsd:

  • autofsd is an RPC server which answers file system mount and umount requests from the autofs file system. It uses local files or name service maps to locate file systems to be mounted. Upon receiving a map argument from a client, the server will attempt to verify if it is executable or not. If autofsd determines the map has an executable flag, the server will append the client's key and attempt to execute it. By sending a map name that is executable on the server, and a key beginning with a semicolon or a newline followed by a command, unprivileged users can execute arbitrary commands as the superuser. The problem occurs when the server appends the key to the map and attempts to execute it by calling popen. Since popen executes the map and key you specify by invoking a shell, it is possible to force it into executing commands that were not meant to be executed.

(RSI.0010.10-21-98.IRIX.AUTOFSD, http://geek-girl.com/bugtraq/1998_4/0142.html.)

Also, like system(), popen() is vulnerable to environment variable attacks. Local attackers may be able to pass commands to the shell or launch malicious programs by altering the Input Field Separator and the $HOME and $PATH environment variables.

To foil such attacks, you can access, manipulate, and hard-code shell environment variables from C with the following functions, all available from the standard library (stdlib.h):

  • getenv()—Use this to get an environment variable.

  • putenv()—Use this to either change or add an environment variable.

  • setenv()—Use this to either change or add an environment variable.

Just how hardcore an approach to take on the environment is debatable, but remember that your C program inherits its environment variables from the shell by which it was executed. If you don't specify sensitive variables, you can inadvertently allow attackers to materially affect your program's execution. (Spafford and Garkfinkel recommend cleaning the environment completely and explicitly creating a new one.)

Table 16.2 describes important shell variables and what they represent.

Table 16.2. bash Environment Variables and What They Mean

VariablePurpose
$-Stores the current shell's flags.
$!Stores the PID of the last command executed in the background.
$#Stores the number of positional parameters ($1, $2, $3, and so on).
$$Stores the PID of the current shell.
$0Stores the name of the program currently being executed.
$CDPATHIdentifies the search path used when you issue the cd(change directory) command.
$HOMEIdentifies the location of your home directory.
$IFSThis variable (Internal Field Separator) stores the character used for field separation.
$LIBPATHIdentifies the search path for shared libraries.
$LOGNAMEStores your username.
$MAILStores the location of your mailbox. From this, the shell knows where to find your mail.
$PATHStores a list of all directories that the shell will search when looking for commands.
$PS1Identifies what your system prompt will look like. For example, on my machine, the PS1 variable is set to $.
$SHACCTStores a filename (a file that is writeable by the instant user) that stores an accounting record of all shell procedures.
$SHELLStores the shell's path.
$TERMIdentifies the current terminal type. Your terminal type can be very important. UNIX uses this to determine how many characters and lines to display per screen.
$TIMEOUTStores the number of minutes of inactivity before which the shell exits.
$TZIdentifies the current time zone.

From C, you can access the total environment (all variables currently set) using environ. As explained on the environ (5) man page:

  • An array of strings called the 'environment' is made available by exec(2) when a process begins. By convention these strings have the form 'name=value'.

In the UNIX Programming FAQ, Andrew Gierth offers an example program that grabs all currently set environment variables and prints them out (similar to printenv and env) using environ:

#include <stdio.h>
     extern char **environ;
     int main()
     {
         char **ep = environ;
         char *p;
         while ((p = *ep++))
             printf("%s
", p);
         return 0;
     }

In Perl, hard-code your environment variables at the top, before processing data, like this:

$ENV{"HOME"} = 'your_desired_home';
$ENV{"PATH"} = 'your_desired_path';
$ENV{"IFS"} = '';

Note

Failure to specify environment variables or check their length can result in C/C++ buffer overflows. xdat on AIX 4 didn't check the length of $TZ, for example, and the resulting overflow bought attackers root access. In a similar vein, in a bug discussed below, setuid utilities in KDE failed to check the length of $HOME.

open() in Perl

open() is a native Perl function that opens files. As explained in the Perl perlfunc documentation, it…

  • …opens the file whose filename is given by EXPR, and associates it with FILEHANDLE. If FILEHANDLE is an expression, its value is used as the name of the real filehandle wanted.

However, you can also use open() to open a process (a command):

  • If you open a pipe on the command "-", i.e. either "|-" or "-|", then there is an implicit fork done, and the return value of open is the PID of the child within the parent process, and 0 within the child process.

Here's an example of using open() to open a file for processing:

open(DATABASE, "mydatabase.txt");
    while(<DATABASE>) {
        if(/$contents{'search_term'}/gi) {
          $count++;
          @fields=split('!:!', $_);
          print "$fields[1] $fields[2] $fields[3]
";
          }
    }
close(DATABASE);

Here's an example of using open() to open a process:

open(PS, "ps|") || die "Cannot open PS
$!";
while (<PS>) {
  if(/pppd/) {
  $count++;
  @my_ppp = split(' ', $_);
  kill 1 $my_ppp[0];
  print "Your PPP process [PID $my_ppp[0]] has been terminated!
"
    }
}
close(PS);
if($count==0) {
   print "There is no PPP process running right now
";
}

To open a process using open() without invoking the shell, try doing this instead:

open(PS, "|-") || exec("ps", "-a");
while (<PS>) {
  if(/pppd/) {
  $count++;
  @my_ppp = split(' ', $_);
  kill 1 $my_ppp[0];
  print "Your PPP process [PID $my_ppp[0]] has been terminated!
"
    }
}
close(PS);
if($count==0) {
   print "There is no PPP process running right now
";
}

Note

Note that problems inherent in invoking the shell are not limited to C and Perl. You should exercise care when performing these tasks in any language. (For example, in Python, if you fail to apply adequate controls, you'll see equally negative results with os.system() and os.popen().)

eval (Perl and shell)

eval is a function available in shells and Perl (typically invoked as evalexpression). As explained in the Perl documentation:

  • EXPR [expression] is parsed and executed as if it were a little Perl program. It is executed in the context of the current Perl program, so that any variable settings, subroutine or format definitions remain afterwards. The value returned is the value of the last expression evaluated, or a return statement may be used, just as with subroutines.

eval will execute commands, all arguments passed to such commands, and even additional, sequential, or piped commands. Using eval is therefore quite risky and offers attackers an opportunity to try a wide range of attacks.

exec() in Perl

The exec() function allows you to execute external commands. As explained in the perlfunc documentation:

  • The exec() function executes a system command AND NEVER RETURNS. Use the system() function if you want it to return. If there is more than one argument in LIST, or if LIST is an array with more than one value, calls execvp(3) with the arguments in LIST. If there is only one scalar argument, the argument is checked for shell metacharacters. If there are any, the entire argument is passed to /bin/sh -c for parsing.

This is risky. exec will execute the command, all arguments passed to it, and even additional, sequential, or piped commands. For this reason, if you use exec (not recommended), enclose each individual argument in quotes like this:

exec 'external_program', 'arg1', 'arg2'

This will prevent attackers from passing arguments (or commands) onto the list.

Buffer Overruns

Buffer overruns are still another example of how user input can materially alter your program's execution and performance. When you write C programs, be sure to use routines that provide buffer boundary checking. If you don't, attackers may be able to overrun the buffer, causing your program to fault. This can offer attackers an opportunity to execute malicious code.

For example, consider gets(), which is available via the standard I/O library (stdio.h) and provides a mechanism to read a line of user input. As explained on the fgetc man page:

  • gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with ''. No check for buffer overrun is performed.

Here's an example of gets() in use when the character buffer is set to 20:

/* gets_exa,ple.c – Why not to use gets() */
#include <stdio.h>

void main() {

    char username[20];
    printf("Please enter your username:");
    gets(username);
    printf("%s
", username);

    }

When run, gets_example reads in username and spits it back out:

linux6$ gets_example
Please enter your username:   anonymous
anonymous
linux6$

But what if the user doesn't enter 20 characters or less? What if he floods gets_example with garbage like this:

linux6$ gets_example
Please enter your username:   anonymousaaaaaaaaaaaaaaaa555555555555555555
5555555555555555555555anonymousaaaaaaaaaaaaaaaa555555555555555555555555
5555555555555555
Bus error (core dumped)
linux6$

Or even this:

linux6$ gets_example
Please enter your username:   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Segmentation fault (core dumped)
linux6$

In both cases, gets_example core dumps because, as explained on the gets() man page…

  • …it is impossible to tell without knowing the data in advance how many characters gets() will read and… gets() will continue to store characters past the end of the buffer.

Attackers search high and low for such holes to exploit so they can run malicious code in unintended memory space.

In addition to gets(), avoid using any of the following routines:

  • fscanf()—Reads input from the stream pointer stream. In many instances, you can use fgets() instead.

  • realpath()—Expands all symbolic links and resolves references to '/./', '/../', and extra '/' characters in the null-terminated string named by path.

  • scanf()—Reads input from the standard input stream stdin. Try using fgets() first to get the string and then use sscanf() on it.

  • sprintf()—Writes to the character string str, but does not check the string's length. Try snprintf() instead.

  • strcat()—Concatenates two strings, and appends the src string to the dest string, but does not check string length. Use strncat() instead.

  • strcpy()—Copies a string pointed to be src to the array pointed to by dest, but does not check string length. Use strncpy() instead.

A sobering example of how buffer overruns can jeopardize your system is the sperl5.003 bug, evident on Red Hat Linux 4.2. suidperl is a tool for securely running setuid Perl scripts. In May 1997, CERT reported that…

  • …due to insufficient bounds checking on arguments which are supplied by users, it is possible to overwrite the internal stack space of suidperl while it is executing. By supplying a carefully designed argument to suidperl, intruders may be able to force suidperl to execute arbitrary commands. As suidperl is setuid root, this may allow intruders to run arbitrary commands with root privileges.

The problem arose in a function using sprintf(). To see a detailed analysis of that hole, and to test attack code that demonstrates how attackers exploit buffer overruns, go to http://www.ryanspc.com/exploits/perl.txt.

Other interesting examples include

  • Netscape Communicator 4.07-4.5 Buffer Overrun—Dan Brumleve found a buffer overrun in specified Communicator versions. When Communicator receives an unknown MIME type, it generates a dialog box that offers you various options. The function that creates the dialog box message uses sprintf() with a 1KB buffer. Remote Webmasters can use the exploit to execute arbitrary commands on your box. The attack turns Communicator into an interactive shell for remote attackers. To experiment with this exploit, get the source at http://www.shout.net/nothing/buffer-overflow-1/view-buffer-overflow-1.cgi.

  • rpc.mountdrpc.mountd is a Remote Procedure Call (RPC) server that answers a client request to mount a file system (part of NFS). In August 1998, independent researchers found a buffer overrun in rpc.mountd that allowed remote attackers to gain privileged access to the target. Check out the explanation and source code at http://pulhas.org/exploits/Linux/mountd4.html.

  • kde (K Desktop Environment)—Catalin Mitrofan found an overflow/environment weakness in kde on Debian. By overloading the HOME and X environment variables, attackers can get high enough access to read /etc/shadow. Get the code at http://hysteria.sk/lists/bugtraq/msg00481.html.

Check the following links to learn more about buffer overflows:

About User Input in General

Try as you might, you can never anticipate every possible combination of characters in a user's input. Most users will input appropriate strings, or those they think are appropriate. But crackers will try exotic combinations, looking for weaknesses in your program. To guard against such attacks, take the following steps:

  • Ensure that your code uses only those routines that check for buffer length. If it contains routines that don't, insert additional code that does.

  • Ensure that you explicitly specify environment variables, initial directories, and paths.

  • Subject your code to rigorous testing. Try overflowing the stack, pushing additional commands onto the argument list, and so on. Essentially, try to crack your own program.

  • In Perl scripts, screen out metacharacters and validate all user input by enforcing rules that allow only words, as in ~ tr/^[w ]//g. Note: Many tutorials suggest that you explicitly define forbidden characters (that which is not expressly denied is permitted). Try to avoid doing this. The favored approach is to explicitly define approved characters instead (that which is not expressly permitted is denied). This method is more reliable.

  • Allowing variable interpolation is very dangerous. Therefore, use single quotes whenever possible. (Any named variable used in a double-quote string is interpolated.)

  • Also, use taintperl, which forbids the passing of variables to system functions. taintperl can be invoked in Perl 4 by calling /usr/bin/taintperl, and in Perl 5 by using the -T option when invoking Perl (as in #!/usr/bin/perl -T).

Paths, Directories, and Files

When you're writing CGI programs, always specify absolute paths. This will prevent attackers from tricking your script into executing an alternate program with the same name.

For example, never do anything like this:

# set up a directory variable
$DIR='pwd';
chop($DIR);
# and then later on…
sub some_function {
    open(EXTERNAL_SCRIPT, "$DIR/myprogram.pl|);
}

Never use relative paths, either. Relative paths point to locations relative to the current directory. Consider this script:

open(DATABASE, "search/data/clients.dat|");
    while(<DATABASE>) {
     if(/$contents{'search_term'}/gi) {
          $count++;
        print "$fields[5] $fields[6] $fields[7]<br>
";
         }
    }
    close(DATABASE);
    if($count < 1) {
    print "No matches!
";
}

This doesn't identify a hard path. If you moved this script, the path leading to clients.dat would change:

  • In /var/http, the script points to /var/http/search/data/clients.dat.

  • In /etc/http, the script points to /etc/http/search/data/clients.dat.

Instead, point to the absolute path, like this:

open(DATABASE, "/var/http/ourcompany.net/search/data/clients.dat");
    while(<DATABASE>) {
     if(/$contents{'search_term'}/gi) {
          $count++;
        print "$fields[5] $fields[6] $fields[7]<br>
";
         }
    }
    close(DATABASE);
    if($count < 1) {
    print "No matches!
";
}

This way, there's no ambiguity. The script points to one file only: /var/http/ourcompany.net/search/data/clients.dat.

Never deviate from this rule, even when launching simple programs. For example, suppose you did this:

system("date");

Or even this:

$mydate='date';

If an attacker can alter $PATH and point to an alternate date, your script will execute it. If you're dead set on executing programs in this manner, try this instead:

system("/bin/date");

Or this:

$mydate='/bin/date';

Also, consider hard-coding your initial working directory at startup. For this, use chdir.

chdir()

chdir(), available in C from unistd.h and also a native Perl function, changes the current directory. It can return many errors that might alert you to problems, like whether the target actually exists. As an additional measure, consider following your chdir() with an lstat(). This will verify that the target is actually a directory as opposed to a symbolic link.

Files

If your CGI programs create or open files, observe these rules:

  • Always include error-handling code to warn you if the file isn't actually a file, cannot be created or opened, already exists, doesn't exist, requires different permissions, and so on.

  • Watch which directories you use to create or open files. Never write a file to a world-writeable or world-readable directory.

  • Always explicitly set the file's UMASK.

  • Set file permissions as restrictively as possible. If the file is a dump of user input, such as a visitor list, the file should be readable only by the processes that will engage that file.

  • Ensure that the file's name does not have metacharacters in it, and if the file is generated on-the-fly, include a screening process to weed out such characters.

Other Interesting Security Programming and Testing Tools

Finally, Table 16.3 lists some interesting tools that can help you test your work.

Table 16.3. Interesting Programming and Testing Tools

VariablePurpose
lclintA lint-like checker for ANSI C that checks risky data sharing, ignored return values, null values, memory management errors, and much, much more. For a description of lclint, go to http://www.doc.ic.ac.uk/lab/cplus/lclint/guide.html. To get lclint, go to ftp://ftp.sds.lcs.mit.edu/pub/lclint/guide.tar.gz.
mem_testA library for finding memory leaks in C programs. Get it at http://members.iquest.net/~jbuchana/mem_test.html.
C InsideA source code viewer that lets you selectively examine the results of preprocessing to determine what macros really expand to. Get it at http://www.thinkage.on.ca/shareware/.
GNU NanaA free library providing improved support for assertion checking and logging in C and C++. Learn more at http://www.cs.ntu.edu.au/homepages/pjm/nana-home/.
PlumberA tool for identifying memory leaks in C programs. Learn more at http://home.earthlink.net/~owenomalley/plumber.html.
ObjectManualGenerates HTML documentation for your C++ programs on-the-fly, (especially useful if you're doing professional development). http://www.obsoft.com/Product/ObjMan.html.
DOC++A tool for generating HTML documentation for your C/C++/Java programs on-the-fly (especially useful if you're doing professional development or when you're accountable for the docs).
cgihtmlA library for writing HTML out from C programs (useful when you don't want to bother coding HTML parsing routines yourself). To get it, go to http://www.eekim.com/software/cgihtml/.
MIME++A C++ class library for parsing, creating, and editing messages in MIME format. Also, it can streamline your work in many instances. Get it at http://www.hunnysoft.com/mimepp/.
LatroScans remote Windows hosts for insecure Perl installations (useful for when you establish a heterogeneous intranet). Get Latro at http://language.perl.com/news/latro-announce.html.
SCATA tool and Application Programming Interface (API) to maintain client state. It is possible to integrate DES (and perhaps PGP or even RSAREF) into SCAT routines. Check out SCAT at http://www.btg.com/scat/scat.html.
msystem (by Matt Bishop)Offers secure versions of system(3), popen(3), and pclose(3). Check out msystem at ftp://coast.cs.purdue.edu/pub/tools/unix/msystem.tar.Z.
crashmeA tool for testing your operating environment software's robustness. In certain cases, it can reveal weaknesses in your programs. Check out crashme at ftp://coast.cs.purdue.edu/pub/tools/unix/crashme/.
showidA shell script that records and reports the UID and GID of program while it is executing. Check out showid at ftp://coast.cs.purdue.edu/pub/tools/unix/show_effective_uid.
worm-srcThe source code to the Internet Worm, an excellent example of how buffer overruns (and other attacks) operate. Get it at ftp://coast.cs.purdue.edu/pub/tools/unix/worm-src.tar.gz.
PAMPluggable Authentication Modules allow you to alter how Linux applications perform authentication without actually rewriting and compiling them. Learn more at http://www.interweft.com.au/other/pam/pam.html.
CGIWrapA gateway program that allows general users to use CGI scripts and HTML forms without compromising the security of the http server. Scripts run with the permissions of the user who owns the script. Check out CGIWrap at ftp://concert.cert.dfn.de/pub/tools/net/cgiwrap/.

Other Online Resources

In addition to the preceding information, there are many online documents that offer excellent secure programming advice. Here are a few:

Summary

Your main aim is to anticipate every possible contingency that can result from your program's use. Approach your code as a cracker would. Visit cracker sites and study how similar programs have been broken in the past. Apply these principles to your own program and see what happens. This is really the only way to be sure.

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

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