![]() |
|
|||||||||||||||
|
||||||||||||||||
|
| Software security principles: Part 3 | ||||
Principle 4: Least privilege 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 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
| ||||||||||||||||||||||||||||||||||||||||||||||||
| About IBM | Privacy | Terms of use | Contact |