IBM®
Skip to main content
    Country/region [select]      Terms of use
 
 
      
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks > Security >
developerWorks
Software security principles: Part 3
e-mail it!
Contents:
Principle 4: Least privilege
Principle 5: Compartmentalization
Next time
Resources
About the authors
Rate this article
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Controlling access: Least privilege and compartmentalization

Gary McGraw (gem@cigital.com), Vice president of corporate technology, Cigital
John Viega (viega@cigital.com), Senior research associate and consultant, Cigital

01 Nov 2000

In this column, Gary and John present a set of 10 principles that can help you remove the majority of security problems from your code. Last time, they talked about providing redundant security measures when appropriate, and how to recover securely from error. This time, they focus on the importance of being stingy when doling out permissions, giving out only what is necessary for as short a time as possible. It is also critical to ensure that a failure in one part of a system does not affect the entire system.

Principle 4: Least privilege
The principle of least privilege states that only the minimum access necessary to perform an operation should be granted, and that access should be granted only for the minimum amount of time necessary.

When you give out access to parts of a system, there will generally be some risk that the privileges associated with that access will be abused. For example, let's say you go on vacation and give a friend the key to your home in order to feed pets, collect mail, etc. While you may trust that friend, there is always the possibility that there will be a party in your house without your consent, or that something else will happen that you don't like.

Whether or not you trust your friend, there's generally no need to put yourself at risk by giving more access than necessary. For example, if you don't have pets, but only need a friend to occasionally pick up your mail, you should relinquish only the mailbox key. While your friend might find a good way to abuse that privilege, at least you don't have to worry about the possibility of additional abuse. If you give out the house key unnecessarily, all that changes.

Similarly, if you do get a house sitter while you're on vacation, you aren't likely to let that person keep your keys when you're not on vacation. If you do, you're setting yourself up for additional risk. Whenever a key to your house is out of your control, there's a risk of that key getting duplicated. If there's a key outside your control, and you're not home, then there's the risk that the key is being used to enter your house. Any length of time when someone has your key and you're not supervising them constitutes a window of time in which you are vulnerable to an attack. You want to keep such windows of vulnerability as brief as possible, in order to minimize your risks.

Another good real-world example appears in the security clearance system of the U.S. government -- the policy of "need to know." If you have clearance to see any classified document whatsoever, you still won't be able to see any secret document that you know exists. If you could, it would be very easy to abuse the secret clearance level. Instead, people are only allowed to access documents that are relevant to those tasks that apply to them.

Some of the most famous violations of the principle of least privilege exist in UNIX systems. For example, on UNIX systems, you generally need to have root privileges to run a service on a port number less than 1024. So, to run a mail server on port 25 -- the traditional SMTP port -- a program needs the privileges of the root user. However, once a program has set up shop on port 25, there is no compelling need for it to ever use root privileges again. A security-conscious program would give up root privileges and let the operating system know that it should never require those privileges again (at least, not until the next run of the program). One large problem with some e-mail servers is that they don't give up their root permissions once they have grabbed the mail port (Sendmail is a classic example). Therefore, if someone finds a way to trick such a mail server into doing something nefarious, it will succeed. For example, if a malicious attacker were to find a suitable stack overflow in Sendmail (see Resources), then that overflow could be used to trick the program into running arbitrary code. Because Sendmail runs with root permissions, any valid attempt by the attacker will succeed.

Another common scenario is that a programmer may wish to access some sort of data object, but only needs to read from the object. Often, however, the programmer actually requests more privileges than necessary, for whatever reason. Generally, the programmer is trying to make life easier. For example, he might be thinking, "Someday I might need to write to this object, and I'd hate to have to go back and change this request."

Insecure defaults might lead to a violation here, too. For example, there are several calls in the Windows API for accessing objects that grant all access if you pass "0" as an argument. In order to get something more restrictive, you'd need to pass a bunch of flags (OR'd together). Many programmers will just stick with the default, as long as it works, because that's easiest.

This problem is starting to become common in security policies for products intended to run in a restricted environment. For example, some vendors offer applications that work as Java applets. Applets constitute mobile code, which Web browsers tend to treat with suspicion. Such code is run in a sandbox, where the behavior of the applet is restricted based on a security policy that a user agrees upon. Vendors rarely practice the principle of least privilege here, because it takes a lot of effort on their part. It's far easier to implement a policy that says, in essence, "let the vendor's code do anything at all." People generally install vendor-supplied security policies, maybe because they trust the vendor, or maybe because it's too much of a hassle to figure out what security policy does the best job of minimizing the privileges that must be granted to the vendor's application.

Principle 5: Compartmentalization
The principle of least privilege works a lot better if your access structure is not "all or nothing." Let's say you go on vacation, and you need a pet sitter. You'd like to limit the sitter's access to just your garage, where you'll leave your pets while you're gone, but if you don't have a garage with a separate lock, then you have no choice but to give the sitter access to the entire house.

The basic idea behind compartmentalization is that we can minimize the amount of damage that can be done to a system if we break the system up into as many isolated units as possible. This same principle is applied when submarines are built with many different chambers, each separately sealed; if a breach in the hull causes one chamber to fill with water, the other chambers will not be affected. The rest of the ship can keep its integrity, and people can survive by making their way to parts of the submarine that are not flooded.

Another common example of the compartmentalization principle is a prison, where the ability of large groups of convicted criminals to get together is minimized. Prisoners don't bunk in barracks, they bunk in cells of one or two. Even when they do congregate -- say, in a mess hall -- other security measures are beefed up to help compensate for the large increase in risk.

In the computer world, it's a lot easier to point out examples of poor compartmentalization than it is to find good ones. The classic example of how not to do it is the standard UNIX privilege model, where security-critical operations work on an "all or nothing" basis. If you have root privileges, you can basically do anything you want. If you don't have root access, there are restrictions. For example, you can't bind to ports under 1024 without root access. Similarly, you can't access a lot of operating system resources directly -- for example, you have to go through a device driver to write to a disk; you can't deal with it directly.

Currently, if an attacker exploits a buffer overflow in your code, that person can make raw writes to disk and mess with any data in the kernel's memory. There are no protection mechanisms preventing that. Therefore, you can't directly support a log file on your local hard disk that can never be erased, which means you can't keep accurate audit information up until the time of a break-in. Attackers will always be able to circumvent any driver you install, no matter how well it mediates access to the underlying device.

On most platforms, you can't protect just one part of the operating system from the others. If one part is compromised, then everything is hosed. A few operating systems, such as Trusted Solaris, do compartmentalize. In such cases, operating system functionality is broken up into a set of roles. Roles map to entities in the system that need to provide particular functionality. One role might be a LogWriter role, which would map to any client that needs to save secure logs. This role would be associated with a set of privileges. For example, a LogWriter would have permission to append to its own log files, but never erase from any log file. Perhaps only a special utility program would be given access to the LogManager role, which would have complete access over all the logs. Standard programs would not have access to this role. Even if you break a program and end up in the operating system, you'll not be able to mess with the log files unless you happen to break the log management program as well. This sort of "trusted" operating system isn't all that common, in large part because this kind of functionality is difficult to implement. Problems like dealing with memory protection inside the operating system create challenges that have solutions, but not ones that are simple to effect.

More so than a lot of the other principles, compartmentalization must be used in moderation. If you segregate each little bit of functionality, then your system will be completely unmanageable.

Next time
In our next installment, we'll talk about two more principles for writing secure software -- simplicity and privacy. The simplicity principle goes beyond just writing straightforward code; in order to minimize security problems, programs should be easy to use, too. The privacy principle dictates that you should avoid giving out unnecessary information, and suggests that sometimes it's O.K. to lie.

Resources

  • Part 1 of this series emphasizes the importance of reinforcing the weakest -- and most susceptible -- parts of your system.

  • Part 2 of this series focuses on defense in depth -- which advocates the use of multiple defense strategies -- and secure failure -- the notion that system failure is not necessarily the same as system vulnerability.

  • See Learning the basics of buffer overflows and Preventing buffer overflows, here on developerWorks, for more information on buffer overflows.

  • In a previous developerWorks article, Assuring your software is secure, Gary and John lay out their five-step process for designing for security.

  • Cigital is a leading authority on software risk management -- check out their site.

  • The developerWorks columns by Gary and John have been updated and expanded in the book Building Secure Software, which also includes plenty of new material. Pick up a copy today -- your users will thank you.

About the authors
Gary McGraw is the vice president of corporate technology at Cigital (formerly Reliable Software Technologies) in Dulles, VA. Working with Consulting Services and Research, he helps set technology research and development direction. McGraw began his career at Cigital as a research scientist, and he continues to pursue research in software engineering and computer security. He holds a dual Ph.D. in Cognitive Science and Computer Science from Indiana University and a B.A. in Philosophy from the University of Virginia. He has written more than 40 peer-reviewed articles for technical publications, consults with major e-commerce vendors including Visa and the Federal Reserve, and has served as principal investigator on grants from Air Force Research Labs, DARPA, National Science Foundation, and NIST's Advanced Technology Program. He can be reached at gem@cigital.com.


John Viega is a senior research associate, Software Security Group co-founder, and senior consultant at Cigital. He is the principal investigator on a DARPA-sponsored grant for developing security extensions for standard programming languages. John has authored over 30 technical publications in the areas of software security and testing. He is responsible for finding several well-publicized security vulnerabilities in major network and e-commerce products, including a recent break in Netscape's security. He is also a prominent member of the open source software community, having written Mailman, the GNU Mailing List Manager, and, more recently, ITS4, a tool for finding security vulnerabilities in C and C++ code. Viega holds an M.S. in Computer Science from the University of Virginia. You can contact him at viega@cigital.com.



e-mail it!
Rate this article

This content was helpful to me:

Strongly disagree (1)Disagree (2)Neutral (3)Agree (4)Strongly agree (5)

Comments?



developerWorks > Security >
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact