When you are programming on the Internet, never skip security. People really do crack Internet systems. If you are on the Internet long enough, someone will try to break into your system. Even the U.S. Department of Justice has had its Web site broken into. By taking appropriate steps to secure your systems and your programs, you (hopefully) will be able to avoid break-ins. Take the word of experience: The time spent up front is well worth it. A single security incident can easily eat up weeks of your time and potentially even millions of dollars if your systems are critical or have confidential information.
If setting up security is going to take an insane amount of time and money, you need to do a risk assessment to determine whether you should go ahead and do it. In a risk assessment, you look at the likelihood of a security breach and the cost of such a breach to your organization-remembering to include indirect factors such as loss of trust by your clients and related lost business-versus the cost of securing your system. Often, this analysis makes the answer obvious.
No security system is ever 100 percent secure. This doesn’t mean that you should just give up on security. Your goal is to secure your system by spending less money on security than a break-in would cost to fix.
People might try to break into your system or program for a variety of reasons. Some do it for fun. Others, unfortunately, do it to steal information or damage your machines. Therefore, security is almost always important. This is especially true of your Internet programs. Because they are likely one of the first things that will be attacked, spend some time thinking about the security implications of any code you are going to deploy. If you are an Internet novice, show your design and code to an Internet expert to have him verify that your program appears to be secure.
You need to remember that your Internet programs should try to deal with security on other fronts. In particular, do your best to defend against attacks from the other employees at your workplace. More security breaches are caused by employees than by crackers out on the Internet. Though you think you can trust your co-workers, keep the number of people who have access to bypass the security mechanisms to an absolute minimum. This strategy improves security and accountability.
When you look at Internet security, you must look from the bottom of the protocol stack (the physical wire) all the way up to end-user applications. Any level can be attacked; therefore, every level needs some security mechanisms in place. This section mainly covers the potential weaknesses of the security mechanisms, so that you can be expecting them and possibly can deal with them in your Internet programs.
One common kind of Internet attack is to eavesdrop on packets as they cross the network, whether on the Internet or your local LAN. Networking people use the term sniffing instead of eavesdropping, however. Just about any computer can be turned into a sniffing device. The scariest thing is that any cracker who becomes root on your UNIX systems can use your UNIX box from her remote cracking site to see packets on your network. By doing this, she can gain more passwords to get into even more machines.
You will notice that the word cracker is used instead of hacker. The term hacker has two meanings. Originally, it was (and still is in many circles) a positive term, meaning “someone who plays with the internal workings of a system.” The media then came along and twisted it to mean “someone who breaks into computer systems.” Many people have recommended the term cracker for the second definition. It’s a good fit, as a safe cracker is comparable to a computer cracker.
Besides preventing the cracker from getting root on your system, you can do a few things to minimize the threat of this attack:
- Don’t use Ethernet networks for your LAN if security is your number one goal because eavesdropping on an Ethernet is easy to do. However, Ethernet is a very good, inexpensive technology for LANs in general.
- If you do use Ethernet, the best thing to do from a security standpoint is to use switching hubs instead of repeated hubs. The advantage is that a single machine on a switched hub doesn’t see traffic on the network that isn’t supposed to go to that machine.
- The final approach is to encrypt traffic as it goes across your LAN or the Internet. This is very labor-intensive and shouldn’t be done lightly. (You will learn much more about encryption later in this chapter.)
One organization that can potentially help you is CERT (Computer Emergency Response Team). CERT makes announcements about what bugs exist in Internet-related software and where to acquire patches to solve the bug problems. If patches aren’t available, it usually at least suggests some workarounds. It also maintains some security tools on its FTP site. You might want to take a look before starting on Internet programming to make sure that the tools you use in your development are secure. Its URL is http://www.cert.org/.
The Web can be fairly secure, completely insecure, or anywhere in between, depending on how it’s configured. Because different Web servers have different security mechanisms, the types of Web security available can range widely from system to system. Most of the popular Web servers (for example, NCSA, Apache, and Netscape Communications/Commerce Server) offer reasonably good security if configured correctly.
One of the security options controls whether someone can upload files via the PUT command. I strongly recommend turning off PUT unless you absolutely need it and fully understand what it’s doing. You can still get information via the Common Gateway Interface (CGI). CGI is another optional feature to which you might want to restrict access on the server. Poorly written CGI programs can create large security holes, as discussed later in this chapter.
If you need advanced security, you can run one of the Web servers that offers encryption capabilities, such as the Apache/SSL, Open Market Secure Web Server, NCSA, or Netscape FastTrack Server. It won’t solve all of your security problems, but it definitely can help, especially against packet sniffing attacks.
Over the 20-plus years that the Internet has been in existence, many security bugs have arisen in Internet programs. The same types of security bugs, however, tend to crop up over and over again.
Preventing Buffer Overflow
The most common bug in Internet programs involves buffers. Internet programs must be prepared to accept input of any length; the mistake that programmers commonly make is to assume that the input has a maximum length and then write it into a fixed-length buffer when the input exceeds that maximum length. Even this can be stopped automatically if your compiler inserts code into the executable to make sure that the buffer (array) boundaries aren’t crossed. However, in C, the traditional Internet programming language, array boundaries aren’t checked.
A cracker can exploit this problem by overwriting the buffer. The excess input then can overwrite the program that exists beyond the end of the buffer. By this overwriting, the cracker then can have the target system execute his own code. The hard part of this attack for the cracker is that he must change his attack for every different type of machine, if not every version of a particular operating system.
To prevent this problem, you must make sure that your buffers don’t overflow under any circumstance. The first solution is to always dynamically allocate your buffers as needed. Some languages (for example, Perl) can automatically do this for you. Another approach is to look at the size of the input; if it’s too long, you treat it as an error or you truncate it. Each of these methods works if implemented correctly, but you need to decide which is the best method for your application.
Preventing Shell Command Attacks
Another common mistake is to allow a cracker to send commands to a shell. This problem occurs when the program accepts some input and then uses it in a shell command without performing adequate checking. On UNIX, it’s absolutely critical to eliminate some special characters, such as backticks (`) and quotes (“). The characters in UNIX that are safe to pass to a shell are all alphanumeric: underscore (_), minus (-), plus (+), space, tab, forward slash (/), at (@), and percent (%). For DOS and other systems, a different set of characters might be valid. In the CGI section of this chapter, I come back to this security problem and explain some ways to solve it for CGI programs.
Changing the Root Directory
One way to minimize all Internet security programming problems is to use a chrooted environment on a UNIX system to run the server program. If you are not on UNIX, this method is not possible and you might want to go on to the next section. The name comes from the chroot (change root) command in UNIX, which changes the root directory for the program. Setting up this kind of environment correctly is somewhat troublesome, but the security benefits can be huge if security is critical for your program. If a cracker does break through your program’s security, he will be in a “rubber room” environment on your system-without access to the real operating system files or anything else you don’t want to expose. This isolation will enormously reduce his ability to cause damage.
If your program works with confidential or sensitive information, it’s critical that your Internet programs can keep that information out of the hands of people who shouldn’t see it. Beyond your program’s security, you need to protect the data from access outside of your program. For example, you might have a program that receives credit card numbers. If a cracker breaks into your system and becomes root, he then can look at the file where you have stored the credit card numbers. A solution to this problem is to keep all this data encrypted whenever you store it. (Before you ignore this occurrence as being unlikely, you should know that it has already happened to a major Internet service provider.)
It is a good idea to have your program demand absolute security by keeping an audit trail; sending this audit trail to a different machine (possibly via syslog) also is advisable. With this method, if the machine that runs your program is compromised, you still have the audit trail from which to recover (as well as a backup, hopefully). Audit trails also are good for finding program bugs and abnormal use patterns, which are often a sign of compromise or attempted compromise. On the negative side, some audit trails can use an enormous amount of disk space and are hard to find useful information from.
Java is probably the most secure of all of the Internet programming languages in existence; Java was built from the ground up to be ultra-secure. The Java security model is somewhat complex, but it incorporates security on several levels. If it were bug-free, it would be an extremely strong security mechanism that would greatly reduce worries about the security of running programs on the Internet. Unfortunately, so far a couple of bugs have been found in the security protection of the language, which would enable people to circumvent the security. The good news is that they are just bugs, not fundamental flaws in the design, and Sun is quickly addressing them. The other good news is that the bugs haven’t been exploited to cause damage.
Byte Code Verifier
The lowest level of security is the byte code verifier in the Java Interpreter, which implements the Java Virtual Machine. The byte code verifier makes sure that a Java program is valid and doesn’t perform any operations that might enable the program access to the machine underlying the interpreter. Checks performed include checking for code that will overflow or underflow the stack and code that tries to access objects that it isn’t allowed to access.
Sun is trying hard to make sure that its byte code verifier is totally secure. When other vendors release their implementations of the Java Virtual Machine, can you trust their byte code verifiers? I don’t have the answer to this question, but it’s something worth paying attention to.
The byte code verifier is invoked by the class loader (java.lang.ClassLoader). The class loader is responsible for loading classes from whatever source it needs, whether the local disk or across the network. The class loader protects a class from being replaced by another class from a less-secure source.
One level above the Class Loader is the Security Manager. The Security Manager implements the security policy for the running Java programs. Stand-alone Java applications can set their own security policy by overriding the Security Manager and instantiating it. Web browsers instantiate their own Security Managers, and applets aren’t allowed to modify them. Netscape Navigator, in particular, implements a very strict Security Manager. For example, accessing the local disk is impossible from any class loaded from the network. With HotJava and AppletViewer, accessing the local disk is possible for a class loaded off the network, if the user allows it.
By modifying the Security Manager, you can change the way security is handled in your applications. With the Security Manager, you can control file access. You can control where network connections can be made. You even can control access to threads through the Security Manager.
Writing your own Security Manager is a very advanced topic, worthy of a book in its own right. Needless to say, I won’t cover it here. If you need some material on writing a Security Manager, the book Tricks of the Java Programming Gurus by Sams Publishing has several chapters on how to do it.
Many people are now talking about putting digital signatures on applets (digital signatures are covered later in this chapter). With this system, you will be able to know who wrote a particular applet. Then, if you trust the author, you can relax the limits that the Security Manager imposes. Watch for this technology; it will be important to you, because you probably will need to sign your applets at some point in the future.
You can look at CGI security from several angles. I’ll cover some of the specific programming problems first and then get around to some of the more global issues related to CGI scripts later.
Handling Input to a Shell
First, go back to the passing-commands-to-a-shell problem discussed earlier. Because Perl is the most commonly used language for CGI programs, let’s look at how to fix this problem in Perl. If the input should never be used in a shell command in any way, the version of Perl known as taintperl should be used instead of normal Perl. taintperl is included with the standard Perl distribution and doesn’t enable any input to be used in a shell command unless you go through a troublesome process of untainting it first.
However, in many cases you need to use some of the input as part of a shell command. You have two choices:
- You can write your own error-checking routines. If you do so, I would strongly recommend that you look for characters you know you can trust and then assume that anything else is an error.
- Looking for characters you can trust is a much safer method than the second alternative-looking specifically for characters you know you can’t trust. This method isn’t as safe because you might miss one.
If you think this is too much trouble, you’re right-but you have an easy alternative. Libraries are available that check the input for you automatically, as well as parsing it into an easy-to-use format. For Perl 4, CGI-LIB is an excellent library. It’s available at the following address:
Don’t be confused by the fact that it’s included with the NCSA httpd distribution. It should work with any UNIX-based Web server and possibly any Web server supporting CGI on any platform that has Perl. For Perl 5, you can get CGI.pm at the following address:
CGI Scripts and userid
If you are writing CGI programs and they don’t run, the problem might be due to the security setup on your Web server. Many system administrators allow only specific people to execute CGI scripts for certain directories, to minimize the security exposure. The reason for this is that CGI scripts typically run as the userid of the Web server. A poorly written program (by any user) that can create files on the Web can cause damage to the Web server. If the Web server runs as root on UNIX or administrator on Windows NT, the damage could be even greater. It is never a good idea to run a Web server as either of these two user IDs. Anyway, if your script doesn’t run at all, talk to your Web administrator.
To solve the problem just discussed, many Web administrators install a program called CGI-WRAP. CGI-WRAP runs on a UNIX Web server and is a setuid program that performs a variety of beneficial tasks:
- First, it runs user-written CGI scripts as that user. This scheme protects the Web server from these programs.
- Second, it eliminates all the dangerous characters previously discussed-before the CGI script ever runs-so that the system administrator doesn’t need to worry about whether users are writing scripts in a secure fashion.
- It also has an option to automatically kill off CGI scripts if they use too much CPU time.
If you are on a system using CGI-WRAP, you need to use different URLs, and you might need to modify your program slightly. Find some local document or talk to your Web administrator to find out how.
A common and deadly mistake that many Web administrators and programmers alike have been making recently is to place a copy of Perl itself in a directory where CGI programs can execute. A knowledgeable cracker can use this mistake to do just about anything he wants to your system. The best strategy is to always keep Perl outside any directory from which the Web server can read.