July 26, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Secure Design Principles

  • March 23, 2007
  • By Neil Daswani, Christoph Kern, & Anita Kesavan
  • Send Email »
  • More Articles »

When building a house, there are certain very specific things that a builder will do: roofing shingles are laid so that the higher shingles overlap the lower ones. Flashing is placed over the top of newly installed windows. These specific practices protect the house from water damage, and they flow from a single, general principle: that water needs to run off of a house in waterfall fashion. Similarly, while there are many specific security practices, they flow from a small set of well-accepted principles. Understanding the fundamental principles puts you in the best position to implement specific practices where needed in your own projects.

The Principle of Least Privilege

The principle of least privilege states that a user or computer program should be given the least amount of privileges necessary to accomplish a task. A common example in which the principle of least privilege works in the physical world is the use of valet keys. A valet is someone that parks your car for you when you arrive at a hotel or restaurant. Car manufacturers give buyers special valet keys with the purchase of their vehicle. When the car owner pulls up at a hotel or restaurant, she gives the valet key to the valet to park the car. The valet key only allows the valet to start the car and drive it to its parking spot, but does not give the valet access to open the glove compartment or the trunk where valuables may be kept. The idea is to only give the valet access to those resources of the car necessary to get the job of parking the car accomplished.1

When you design and use software, you should attempt to employ the same kind of mentality with respect to giving programs just enough permissions for the job that they are required to accomplish. If you are designing or implementing a web server that is only responsible for serving static (read-only) marketing pages to web users, the web server should only be given access to the exact set of files that the web server serves to its clients. The web server should not be given privileges to access the company employee database or any other resource that would allow it to do more than serve the marketing pages. By following this approach, if anyone breaks into the web server, the hope is that the most that the attacker will be able to do is read the files that make up the marketing pages, because that is all the web server is able to do. If the web server is configured correctly and not given write access to the files, then you would also expect that the attacker would not be able to deface the web site.2

Unfortunately, in practice, web servers are sometimes given unnecessary privileges that allow them access to parts of the file system that they do not need access to, and that also allow them to modify files. Attackers are able to do an excessive amount of damage when they crack into such web servers because of these elevated privileges.

For instance, if the system administrator were to run SimpleWebServer under the root account,3 then when clients connect to the web server, they would be able to access all files on the system. You might think that this might not be so bad so long as there are no sensitive documents stored in the web server's directory tree. However, due to a vulnerability in SimpleWebServer, an attacker will be able to access all files on the system! We will now illustrate the vulnerability.

Note that in the serveFile() function, SimpleWebServer creates a FileReader object to read the file that the user requested in memory. While you would expect that typical filenames specified by users in the GET request might look like /index.html, /admin/login.php, or even /logs/joe/1.txt, an attacker might issue GET requests that are malicious. For instance, an attacker might issue the following request:

GET ../../../../etc/shadow HTTP/1.0

Due to the way the FileReader constructor works, it will attempt to access the file specified by its string argument relative to the current working directory. As a result, by issuing such a GET request, the attacker would be able to traverse up the directory tree to the root directory, and then access a file such as /etc/shadow, which, on UNIX, contains a list of all usernames and "encrypted" versions of their passwords. Even though the passwords are "encrypted," an attacker may then attempt to mount a dictionary attack against the password file, especially if the password system was not designed well.

To prevent this attack, you need to canonicalize and validate the pathname that the client specifies in the GET request. Writing such code can often be tricky business. The following might be a first-cut implementation at a function that checks the path with the goal of preventing the attack:

String checkPath (String pathname) throws Exception {
                       File target = new File (pathname);
                       File cwd = new File (System.getProperty("user.dir"));
                       String targetStr = target.getCanonicalPath();
                       String cwdStr = cwd.getCanonicalPath();
                        if (!targetStr.startsWith(cwdStr))
                                          throw new Exception("File Not Found");
                       else
                                          return targetStr;
}

Then, you just pass a normalized path to the File constructor in the serveFile() method:

fr = new FileReader (checkPath(pathname));

The checkPath() function first creates a File object called target that corresponds to the pathname that the user requests. Then, it creates a File object called cwd that corresponds to the current working directory. (The call to System.getProperty("user.dir") is used to retrieve the current working directory.) The getCanonicalPath() method is called for each file to normalize the pathnames (i.e., eliminate ".," "..," and other ambiguities in the pathname).4 If the canonicalization fails for any reason, checkPath() will throw an IOException. Finally, the if statement checks to see if the target pathname is at the same level or below the current working directory. If not, checkPath() throws an exception to prevent the case in which an attacker tries to access a file that is above the web server's current working directory.

The preceding example used the checkPath() function to help contain the impact if the web server is run as root. Validating the input in the HTTP request prevents an attacker from being able to access files (including those accessible only by root) that are above the directory from which the web server is run. However, if the web server is run as root, an attacker could still successfully place HTTP requests for files only accessible to root that are in or below the directory from which the web server is run, even when checkPath() is used to validate the input in the HTTP request. While checkPath() helps contain the damage if the principle of least privilege is ignored, to truly avoid vulnerability, the web server should not be run as root.

Defense-in-Depth

Defense-in-depth, also referred to as redundancy, is the second design principle we will discuss in this article. To start with a common real-world example, consider how some banks protect themselves from bank robbers.

Prevent,Detect, Contain, and Recover

The point of defense-in-depth is to not rely on any one defense to achieve security. Multiple mechanisms can help you achieve more security than just one. Some mechanisms (such as the security guards outside the bank) might help prevent attacks. In the case of a bank robbery, it is usually quite obvious when the robbery is taking place-but in the world of network security, it may not even be clear when an attack is taking place. As such, some mechanisms might help you detect when attacks are taking place. Since it is not always possible to prevent attacks altogether, it is important to deploy mechanisms that help you manage or contain attacks while they are in progress. In some banks, bank tellers are stationed behind bulletproof glass, which helps contain the effect of a bank robbery by working to spare the lives of the bank tellers in the case that violence breaks out. After an attack takes place, you want to be able to recover from the attack, to whatever extent possible. Bank tellers may give the robbers a specially prepared briefcase of cash that will spurt dye on the robber when he opens it. The police will then be able to find the bank robber because the dye can only be removed using special chemicals, which helps create accountability.5 In addition to dye-laced briefcases, banks take out insurance policies to help deal with the financial loss in case the cash cannot be recovered. A good security system, whether it be for physical banks or software information systems, should employ defense-in-depth, and include mechanisms that help to prevent, detect, manage, and recover from attacks.





Page 1 of 5



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel