Item 60: Assume insecurity



Item 60: Assume insecurity

So you've finally got the big J2EE system done, ready for deployment into the production environment, and everybody's really excited. The CEO has promised major bonuses for all parties concerned, and the VP of Development is about ready to pass out from all the kudos and back-slapping congratulations coming her way. It has been a good trip for everyone so far. The system has gone through quality assurance with flying colors (OK, there were a few bugs, but nothing that should stop the system from being deployed into production), and we're ready to go.

The system administrators, ready to put this puppy into place, begin by installing the database software (if it's not already installed), the J2EE container, as well as any third-party software needed to make the whole thing work. They pop in the CDs, accept the defaults, and a few hours later, everything's ready for deploying your code. They go through the deployment script (see Item 14), run through a quick "happy bit" test to make sure everything's in good shape and turn on the monitoring tools (see Item 12), and announce that everything's in place; the system has officially been deployed into production! The crowd goes wild, everybody heads out to the bar for major celebrations, and the company's stock goes up several points on the good news.

Before you break out that second bottle of champagne, however, beware the huge, dangerous assumption that you (and everybody else) are making as you head out the door. You're assuming that the installation defaults are, in fact, acceptable. Certainly, from a performance and/or correctness perspective, the defaults may work, but more importantly, are you sure that the defaults set up by the vendor leave the system secure?

Consider, for a moment, the Oracle database installation. By default, assuming no other steps are taken to correct this, every Oracle database comes with at least one login that everybody knows about: the scott username, tiger password account. It's used as the example account in just about every Oracle book on the planet, and a lot of Oracle database administrators and developers are so used to having it there they forget that every other Oracle developer on the planet knows it's there, too. Similarly, SQL Server ships with the administrative account sa set to a default password of nothing, as in, "just hit Enter when asked for the password" nothing. Other databases follow suit.

Here's an interesting test for you: go through all your company's Oracle and SQL Server database instances, trying scott/tiger and sa/<nothing> and see how many successful logins you get. (Of course, make sure you've notified your system administrators of what you're doing, in case you accidentally trip an intrusion detection system or they happen to catch you wandering through the databases; I'd hate for you to get fired on my advice.) If your company is like many companies, chances are you'll be somewhat shocked and appalled at how many times these default accounts are left turned on.

Worse yet, many times these accounts still have their default permissions turned on, which means that in the case of the standard administrative accounts, you have complete and total access to every database instance installed on that server or cluster. Imagine the chaos you could wreak if you were feeling particularly malicious.[2] Even for those installations where the default logins aren't particularly powerful, what's to stop you from engaging in a denial-of-service attack by filling up the disk with streaming media from some of the Internet's most popular Web sites?

[2] In no way am I suggesting that you actually use this as a means of "getting back at your boss" or the company for insults or slights, real or imagined. But a certain amount of security preparedness means thinking like an attacker, to understand how they will attack your system.

This is a particularly sticky point of building a secure system: you must assume everything is insecure, including any particular point in the system you're building, until you can establish beyond any sort of reasonable doubt that the system is secure against intrusion and attack. Of course, if you trust your vendor when they tell you it's secure, then by all means, go ahead and assume it's secure, but before you do, stop and think: Do you seriously believe the vendor would tell you if it weren't?

The logical next step is to somehow prove the system is secure. You can probably establish a pretty comprehensive checklist of common attacks to try, particularly when aided by the Open Web Application Security Project (OWASP)[3] Top Ten Web Application Vulnerabilities list and other documents. You can test against known attacks that have been historically successful against that vendor's product, and you can even try a few new ones you've thought up along the way. The unfortunate fact is, all this testing still isn't going to prove that the system is secure.

[3] An open-source collaborative effort designed to help organizations recognize and understand the details of securing Web applications and Web Services; see http://www.owasp.org.

The problem is that proving a system is secure is much like proving somebody was planning to commit treason: it's slippery, nebulous, and subject to change from one moment to the next. For example, even if we can prove that the system is secure today, a new vulnerability in the vendor's software could be discovered by the hacking community tomorrow, and suddenly our system has a vulnerability that we don't even know exists, at least not until it's used successfully on our system.

To summarize, there are two laws of enterprise security.

  1. Assume a component is insecure until proven otherwise.

  2. A component can never be proven secure.

So where, exactly, does that leave us? The combination of the two seems to lead to a rather impossible situation.

Welcome to the scary world of enterprise security.

Before you think I'm being a bit paranoid and defeatist about the whole thing, I'm not alone in preaching this particular depressing song; Schneier writes, "In all our years of working in [security], we have yet to see an entire system that is secure. That's right. Every system we have analyzed has been broken in one way or another. There are always a few components that are good, but they invariably get used in insecure ways" [Schneier03, 1].

Where this leaves us is pretty simple: always assume that everything is insecure and that you will be attacked successfully at some point. Given the practical realities of the situation facing us in the current decade, you really have no other choice but to accept this. I realize this isn't an easy pill to swallow—take a moment and gather your composure if you need to—but it is a pill you need to take.

As it turns out, this isn't a final pronouncement of doom, much as it may seem like one. If we again go back to the real world for a moment, it turns out that this is a pretty common state of affairs. Most physical security devices are built and designed in such a way that they can be broken eventually. Think about it: If the bank didn't assume that the burglars could get past the locks on the doors, why would they stick the money into a vault deep inside the building? If the government didn't assume that the enemy could get past the barbed wire fence, why put troops in jeeps with machine guns to patrol the perimeter? If automakers didn't assume that car thieves could get past the locks on the doors, why make the engine start only to a particular key?

In essence, each of these real-world security systems is using a defense-in-depth strategy; rather than placing all the security eggs into the basket at the perimeter, they assume that at each level, the defense can be broken, so another defense needs to be ready just in case. Again, to go back to the bank scenario, a typical bank has several layers of security established to prevent people from walking out with money stolen from the vault. Assuming the bank thief strikes at night, we have locks on the doors, we have cameras watching the doors and windows (to deter the thieves on the idea that we'll be able to identify them and hunt them down), we have sensors tied to alarms at the local police station on each of the doors and windows, and we have a vault made of yard-thick steel with a really hard-to-break lock on the front door. If the bank thief wants to strike during the day instead, we have a guard in the lobby, who may or may not have a gun (more deterrence), the tellers usually stand behind some kind of physical barricade and have some kind of silent alarm they can trip to alert the police, and the vault door can be closed if the thieves aren't quite quick enough.

How do we translate this defense-in-depth concept into an enterprise system? For starters, we abandon the notion that the firewall will make everything secure or that if we keep up on the latest operating system patches, we'll be fine. Simply dropping the Web server into the DMZ is not enough—it's a step, but we have to carry it further, just as having a firewall or a DMZ is just a step.

Some ideas (but certainly not an exhaustive list) to consider for deploying a defense-in-depth strategy follow.

  • Establish a firewall around the core components of the system (HTTP server, database server, and so on).

  • Establish a second firewall behind the "forward" parts of the system (those parts the user's client software must communicate with, typically the HTTP server), thus creating a DMZ. Configure the second firewall to accept traffic only from the HTTP server.

  • Turn off every system service on both the HTTP server and database server; there is no reason, for example, the HTTP server or the database server needs to answer TCP/IP date or echo requests. Ping may be about the only thing you need here as far as TCP/IP basic services, and even that's a matter of debate. For Win32 operating systems, for example, the HTTP server machine really doesn't need MSMQ, the COM+ system application or event system services, infrared monitor service, and so on. Each of these is a potential vulnerability point, and if they're not being used by the server for anything, why have them turned on? Do a similar checklist for Solaris and/or Linux servers—do you really need X running on those production machines? Granted, it's convenient sometimes, but is it worth the potential security risk? Even Telnet should probably be turned off, assuming the system administrators have access to a physical keyboard and monitor to do machine administration. (Yes, I know it's more convenient to have Telnet turned on, but again, can you prove that the machine's Telnet daemon is secure?)

  • Never use the same login credentials within the application as those used to administer the HTTP or database services. In other words, make sure the system administrators and database administrators have different username/password identities than those used within the application; as a corollary, never use those administrative accounts across a remote connection if possible, and never from outside the firewall.

  • Make sure everything on the security perimeter (i.e., somewhere between the servers that users access and the users themselves) has had every single authentication credential checked—turn off the defaults, make sure the passwords selected aren't weak or easy to crack (if possible—there's only so much you can do here when users insist on having "password" as their password), and so on. This goes for every piece of equipment, including routers, hubs, firewalls, proxies, and so on.

As you can see, it's pretty easy to start to get into a paranoid mind-set when thinking about security like this. That's probably a good start.

Remember, we can't stop an attacker—prevention by itself will never guarantee a secure system (see Item 58). Instead, the goal is to slow the attacker down, so that a combination of intrusion detection systems and alert system administrators can detect the attack and take the appropriate reactionary steps, including shutting down accounts and/or systems if necessary (depending on the sensitivity of the system being attacked, which is why you build a threat model in the first place—see Item 59).

One corollary to this item is to follow the principle of least privilege—that is, don't grant anything more than the absolute minimum set of rights to your code to execute. For code that accesses a relational database, for example, don't use a database account that has rights beyond the simple SELECT/INSERT/UPDATE/DELETE actions. As explained in Item 61, if the code somehow gets smashed by a command injection attack, if the account has rights to manipulate the schema itself, you could end up staring at a database instance with absolutely zero tables in it. Similarly, if the code has rights to the file system and the code again is successfully smashed, you could find yourself with a ton of unrecognizable files (all of which start with the three letters XXX, strangely enough) stored on your server's filesystem.

This attitude doesn't stop with your code, by the way. Apply the principle of least privilege at every opportunity, including that point at which Java meets the underlying operating system. If you install a J2EE container to run as a standalone user-agnostic application (a daemon process under UNIX, a service under Win32), make sure the process executes as the least-privileged user possible, even if this means creating a user account specifically for that container.

(Under NT/Win2K/XP, by the way, the account used to run the process is configurable through the Services snap-in—bring it up, find the service in question, and bring up that service's Properties page, typically by right-clicking it and selecting Properties from the menu. One of the tabs in the Properties page is called Log On, and this is where you can configure the account used to execute the process. Generally speaking, for a production system, you won't want to use the LocalSystem account, since this account is used for other services, and you'll want to tune the rights assigned to the account to be as minimal as possible. For more information on Win32 security, including all the rights and what they mean, I highly recommend Programming Windows Security [Brown].)