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 2
e-mail it!
Contents:
Principle 2: Defense in depth
Principle 3: Secure failure
Next time
About the authors
Rate this article
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Defense in depth and secure failure

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 series of articles, Gary and John offer the 10 most important points to keep in mind when designing and building a secure system. Part 1 explored the importance of reinforcing the weakest parts of a system. This time, they look at the next two principles: 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.

In this series, we present a set of principles that can help you squash the majority of security problems in your code. Last time, we talked about how to identify and secure the weakest links in a system. This time, we examine how it is important to provide redundant security measures, and why you should be careful not to rely on insecure behavior when parts of a system fail.

Principle 2: Defense in depth
The idea behind defense in depth is to manage risk with multiple defensive strategies, so that if one layer of defense turns out to be inadequate, another layer of defense will, ideally, prevent a full breach. This principle is well-known, even beyond the security community; for example, it is a famous principle for programming language design:

Defense in Depth: Have a series of defenses so that if an error isn't caught by one, it will probably be caught by another. (From Bruce MacLennan's Principles of Programming Languages. See Resources.)

Let's return to our example of providing security for a bank. Why is the typical bank more secure than the typical convenience store? Because there are many redundant security measures protecting the bank -- the more measures it has, the more secure it is. Security cameras alone are usually enough of a deterrent. But if attackers don't care about the cameras, then a security guard is there to physically defend the bank. Two security guards will provide even more protection. But if both our guards get shot by masked bandits, then at least there's still a wall of bulletproof glass and electronically locked doors to protect the tellers. If the robbers do happen to kick in the doors, or guess the PIN, at least the robbers will only have easy access to the registers, because we have a vault protecting the rest. Ideally, the vault is protected by several locks, and cannot be opened without the presence of two individuals who are rarely at the bank at the same time. And as for the registers, they can be equipped with dye-emitting mechanisms that mark the bills.

Of course, having all these security measures in place does not ensure that our bank will never be robbed successfully. Bank robberies do happen, even at banks with this much security. Nonetheless, it's pretty obvious that the sum total of all these defenses results in a far more effective security system than any one defense alone.

This may seem somewhat contradictory to the previous principle, because we are essentially saying here that multiple defenses can be stronger than the strongest link. However, there is no contradiction; the principle of "securing the weakest link" applies when components have security functionality that does not overlap. But when it comes to redundant security measures, it is indeed possible that the total protection offered is far greater than the protection offered by any single component.

A good real-world example where defense in depth can be useful, but is rarely applied, is in the protection of data that travels between various server components in enterprise systems. Most companies will throw up a corporate-wide firewall to keep intruders out. These companies then assume that the firewall is sufficient, and let their application server talk to their database in the clear. If that data is important, what happens if an attacker manages to penetrate the firewall? If the data is also encrypted, then the attacker won't be able to get at the data without breaking the encryption, or (more likely) breaking into one of the servers that stores the data in an unencrypted form. If we throw up another firewall just around our application, then we can protect ourselves from people who penetrate the corporate firewall. Then they'd have to find a flaw in some service that our application's network explicitly exports; we can keep a tight reign on that information.

Principle 3: Secure failure
Any sufficiently complex system will have failure modes. These are pretty unavoidable. What are avoidable are security problems related to failures. The problem is that when many systems fail in any way, they revert to insecure behavior. In such systems, attackers only need to cause the right kind of failure, or wait for the right kind of failure to happen.

The best real-world example we've ever heard is one that bridges the real world and the electronic world -- credit card authentication. Big credit card companies such as Visa and MasterCard spend lots of money on authentication technologies to prevent credit card fraud. Most notably, whenever you go into a store and make a purchase, the vendor swipes your card through a device that contacts the credit card company. The credit card company checks to see if the card is known to be stolen. More amazingly, the credit card company analyzes the requested purchase in the context of your recent purchases, and compares the patterns to the overall trends of your spending habits. If their engine senses anything fairly suspicious, the transaction is denied.

This scheme is remarkably impressive from a security point of view -- until you note what happens when something goes wrong. What happens if the credit card stripe somehow gets demagnetized? Does the vendor have to say, "I'm sorry, your card is not valid because the stripe is broken"? No. The credit card company still supplies the vendor with manual machines that create an imprint of your card, which the vendor can send to the credit card company for reimbursement. If you have a stolen card, it probably won't be authenticated at all. Merchants probably won't even ask you for your ID.

There used to be some security in the manual system, but it's gone now. Before computer networks, you'd probably be asked for your ID to make sure the card matched your license. As another precaution, if your number was on a periodically updated list of bad cards in the area, the card would be confiscated. Also, the vendor would probably also check your signature. These techniques aren't really necessary anymore -- as long as the electronic system is working! If it somehow breaks down, then, at a bare minimum, those techniques ought to come back into play. In practice, however, they don't. Credit card companies feel that failure is way too uncommon in credit card systems to ask vendors to remember a complex procedure when it happens.

When the system fails, the behavior of the system is less secure than typical behavior. Unfortunately, it's really easy to cause the system to fail. For example, it's easy to ruin the stripe of a stolen credit card by running it across a large magnet. In doing so, a thief has more or less minted an arbitrary amount of money, as long as they use the card for small purchases (better validation is often required for large purchases). The good thing about this scheme from a thief's perspective is that failure rarely results in the thief getting caught. Someone can use the same card for a long time this way, with little risk.

Why do credit card companies use such a brain-dead fallback scheme? The answer is that these companies are good at risk management. They can absorb a fairly large amount of fraud, as long as they keep making money hand over fist. They also know that the cost of deterring this kind of fraud would not be justified, because the amount of fraud actually committed is relatively low. (A lot of factors affect this decision, including costs and public relations issues.)

Plenty of other examples pop up in the digital world. Often, the problem arises because of a need to support legacy versions of software that isn't secure. For example, let's say that the original version of your software was naïve, and did not use encryption at all. Now you want to fix the problem, but you have established a large user base. In addition, you have deployed many servers that probably won't be upgraded for a long time. The newer, smarter clients and servers need to interoperate with older clients that aren't updated with the new protocols. You'd like to force old users to upgrade, but you haven't planned for that. Legacy users aren't expected to be such a big part of the user base that it will really matter anyway. What do you do? Have clients and servers examine the first message they receive from one another, and figure out what's going on from there. If we are talking to an old piece of software, then we don't perform encryption.

Unfortunately, a wily hacker can force two new clients to each think the other is an old client by tampering with data as it traverses the network. And worse, there's no way to get rid of the problem while still supporting full (two-way) backward compatibility.

A good solution to this problem is to design in a forced upgrade path from the beginning; the client detects that the server is no longer supporting it. If the client can securely retrieve patches, it does so. Otherwise, it tells the user that they must obtain a new copy manually. Unfortunately, it's important to have this sort of solution in place from the very beginning, unless you don't mind alienating your early adopters.

Most implementations of Remote Method invocation (RMI) have a similar problem. When a client and server want to communicate over RMI, but the server wants to use SSL or some other encryption protocol, the client may not support the protocol the server wants to use. When that's the case, the client generally downloads the proper socket implementation from the server at runtime. This constitutes a big security hole, because the server has not been authenticated at the time that the encryption interface was downloaded. An attacker could pretend to be the server, installing his own socket implementation on each client, even when the client already has proper SSL classes installed. The problem is that if the client fails to establish a secure connection with the default libraries (a failure), it will establish a connection using whatever protocol an untrusted entity gives it, thereby extending trust.

Next time
In our next installment, we'll discuss two more of our 10 design principles. The first is a classic -- the principle of least privilege, whose basic premise is to limit access to the minimum required to do the job, and to extend that access for as short a time as possible. The second is almost as well-known, the compartmentalization priniciple, which states that you should seek to prevent damage from spreading by building walls between parts of your system.

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