I picked up a free copy of 19 Deadly Sins of Software Security at a conference last year. I didn't expect the book to be good because it was a free giveaway item from one of the the vendor booths. But I paged through it on the flight home, and I was pleasantly surprised. It's actually quite good.
Software security isn't exactly my favorite topic, so holding my interest is no mean feat. It helps that the book is mercifully brief and to the point, and filled with practical examples and citations. It's an excellent cross-platform, language-agnostic checksheet of common software security risks.
Here's a brief summary of each of the 19 sins, along with a count of the number of vulnerabilities I found in the Common Vulnerabilities and Exposures database for each one.
| Affected Languages | Exploit count | ||
| Buffer Overflows | C, C++ | A buffer overrun occurs when a program allows input to write beyond the end of the allocated buffer. Results in anything from a crash to the attacker gaining complete control of the operating system. Many famous exploits are based on buffer overflows, such as the Morris worm. | 3,326 |
| Format String Problems | C, C++ | The standard format string libraries in C/C++ include some potentially dangerous commands (particularly %n). If you allow untrusted user input to pass through a format string, this can result in anything from arbitrary code execution to spoofing user output. | 411 |
| Integer Overflows | C, C++, others | Failure to range check on integer types. This can cause integer overflow crashes and logic errors. In C/C++, integer overflows can be turned into a buffer overrun and arbitrary code execution, but all languages are prone to denial of service and logic errors. | 288 |
| SQL Injection | All | Forming SQL statements with untrusted user input means users can "inject" their own commands into your SQL statements. This puts your data at risk, and can even lead to complete server and network compromise. | 2,225 |
| Command Injection | All | Occurs when untrusted user input is passed to a compiler or interpreter, or worse, a command line shell. Potential risk depends on the context. | 193 |
| Failing to Handle Errors | Most | A broad category of problems related to a program's error handling strategy; anything that leads to the program crashing, aborting, or restarting is potentially a denial of service issue and therefore can be a security problem, particularly on servers. | 80 |
| Cross-Site Scripting (XSS) | Any web-facing | A web application takes some input from the user, fails to validate it, and echoes that input directly back to the web page. Because this code is running in the context of your web site, it can do anything your website could do, including retrieving cookies, modifying the HTML DOM, and so forth. | 2,996 |
| Failing to Protect Network Traffic | All | Most programmers understimate the risk of transmitting data over the network, even if that data is not private. Attackers can eavesdrop, replay, spoof, tamper with, or otherwise hijack any unprotected data sent over the wire. | 26 |
| Use of Magic URLs and Hidden Form Fields | Any web-facing | Passing sensitive or secure information via the URL querystring or hidden HTML form fields, sometimes with lousy or ineffectual "encryption" schemes. Attackers can use these fields to hijack or manipulate a browser session. | 33 |
| Improper use of SSL and TLS | All | Using most SSL and TLS APIs requires writing a lot of error-prone code. If programmers aren't careful, they will have an illusion of security in place of the actual security promised by SSL. Attackers can use certificates from lax authorities, subtly invalid certificates, or stolen/revoked certificates, and it's up to the developer to write the code to check for that. | 123 |
| Use of Weak Password-Based Systems | All | Anywhere you are using passwords, you need to seriously consider the risks inherent to all password-based systems. Risks like phishing, social engineering, eavesdropping, keyloggers, brute force attacks, and so on. And then you have to worry about how users choose passwords, and where to store them securely on the server. Passwords are a necessary evil, but tread carefully. | 1,235 |
| Failing to Store and Protect Data Securely | All | Information spends more time stored on disk than in transit. Consider filesystem permissions and encryption for any data you're storing. And try to avoid hardcoding "secrets" into your code or configuration files. | 56 |
| Information Leakage | All | The classic trade-off between giving the user helpful information, and preventing attackers from learning about the internal details of your system. Was the password invalid, or the username? | 26 |
| Improper File Access | All |
1) There is often a window of vulnerability between time of check and time of use
(TOCTOU) in the filesystem, so an attacker can slip changes in, particularly if
the files are accessed over the network. 2) The "it isn't really a file problem"; you may think you have a file, but attackers may substitute a link to another file, or a device name, or a pipe. 3) Allowing users control over the complete filename and path of files used by the program; this can lead to directory traversal attacks. |
5, 58 |
| Trusting Network Name Resolution | All | It's simple to override and subvert DNS on a server or workstation with a local HOSTS file. How do you really know you're talking to the real "secureserver.com" when you make a HTTP request? | 20 |
| Race Conditions | All | A race condition is when two different execution contexts are able to change a resource and interfere with each other. If attackers can force a race condition, they can execute a denial of service attack. Unfortunately, writing properly concurrent code is incredibly difficult. | 139 |
| Unauthenticated Key Exchange | All | Exchanging a private key without properly authenticating the entity/machine/service that you're exchanging the key with. To have a secure session, both parties need to agree on the identity of the opposing party. You'd be shocked how often this doesn't happen. | 1 |
| Cryptographically Strong Random Numbers | All | Imagine you're playing poker online. The computer shuffles and deals the cards. You get your cards, and then another program tells you what's in everybody else's hands. Random numbers are similarly fundamental to cryptography; they're used to generate things like keys and session identifiers. An attacker who can predict numbers-- even with only a slight probability of success-- can often leverage this information to breach the security of a system. | 5 |
| Poor Usability | All | Security is always extra complexity and pain for the user. It's up to us software developers to go out of our way to make it as painless as it can reasonably be. Security only works if the secure way also happens to be the easy way. | All |
It's true that C and C++ have a heavy cross to bear. But only 3 of the 19 sins can be completely lumped on the plate of K&R. The other 16 apply almost everywhere, to any developer writing code on any platform. It's a sobering thought.
The usability sin is the one that's most interesting to me. Usability is tough under the best of conditions-- and security is the worst of conditions, at least from the user's perspective. It's quite a challenge. There are a few great links in the book on the topic of security usability:
You can certainly find other books that go much deeper on particular aspects of software security. But if you're looking for an excellent primer on the entire gamut of security problems that could potentially afflict your project, 19 Deadly Sins of Software Security is an excellent starting point.
The formatting of the number for 'improper file access' is not correct, or a digit is missing.
Just me on April 19, 2007 1:27 AMThose are two separate numbers.
Matthijs van der Vleuten on April 19, 2007 1:53 AMI would like to point out that although the lack of memory management makes C/C++ a greater risk there is also a risk involved in running software within a framework (.NET, Java). A security problem within the framework immediately affects all software using it (class break).
Daniel on April 19, 2007 2:21 AMHmmm, this is a good list but I have seen many of them covered elsewhere, particularly the web based ones as that's my main area of work. It is the attack vectors that are not widely known (or indeed known at all) that worry me the most.
With regard to usability, security vs. usability is a major issue and I think that many developers and network administrators in particular see them as being competing forces, i.e. if we improve usability by freeing up resources to our users then it is to the detriment of security.
I have seen many examples of this over the past few years, for example overly restrictive network firewall policies which are actually preventing people from doing their jobs correctly.
Security is very important but it is not the absolute priority for software development, and should never become a barrier to people effectively doing their jobs IMHO.
john on April 19, 2007 5:43 AM> A security problem within the framework immediately affects all software using it (class break).
Sounds like someone needs to re-read Ken Thompson's "Reflections on Trusting Trust" (http://www.acm.org/classics/sep95/) - C/C++ are in absolutely no sense of the phrase immune to the same class breaks you point out as an Achilles' Heel when using Java/.Net.
To me, it's a pretty silly reason to avoid managed frameworks - you're trading up from a hypothetical risk to a real one while trying to do some mental gymnastics to convince yourself that you're safer (because you've got C Wizards working on it instead of C# Flunkies, maybe?).
Security's tough and made tougher by the fact that applications sometimes aren't built with security in mind. You don't need to subscribe to Bugtraq but bolting security on after the fact sure feels like it's an order of magnitude more difficult than designing it in to the initial product.
Information leakage is by far the worst, I know of several times when the customer support where I work just gave away passwords to a customer without really checking if that was indeed the correct customer.
A programmer can build in whatever security checks but if the people that manage the whole thing don't understand or don't care about security it won't matter.
Dave:
I don't think Daniel's telling anyone to avoid the framework, nor is he doing some "mental gymnastics" to convince himself he's safer. In fact, he explicitly pointed out that there was a greater risk involved when using C/C++.
His point, and I think it's very valid, is that with C# you get a much safer language on a slightly less-safe platform. Any insecurities in the OS itself that would affect a C/C++ program will also affect a C# program, plus you have the additional "leaky abstraction" of the .NET Framework.
It's no reason to avoid .NET; you just might want to be a bit more diligent about applying the latest security patches.
Eam on April 19, 2007 6:36 AMThere's a great series of interviews and show 006 is with M Howard who is an author of this book.
http://www.cigital.com/silverbullet/
howserx on April 19, 2007 6:56 AMHaving used Delphi/Object Pascal for years, I was always wondering about why the C++ developers yammered about their memory getting stepped on, and when I learned that it was due to putting a 12 character string into a 10 byte field, I was stunned.
Anyways, it would be interesting to see a framework developed that would not let 'net developers do dangerous things quite so easily.
"Anyways, it would be interesting to see a framework developed that would not let 'net developers do dangerous things quite so easily."
Internet Explorer qualifies, if you drop the word "dangerous."
In the "Unauthenticated Key Exchange" section, it says "you'd be shocked how often this doesn't happen." Apparently, it only has happened once? That is a little bit shocking.
David on April 19, 2007 8:14 AMWhat happens of I do enter HTML in the comment box? Code injection? :-).
The most frequent of these vulnerabilities are impossible in a decent language like Ada, or our own derivative Carmen. Buffer overflows are impossible per library and code injections are impossible with a little code you have to write, but that can be completely ignored from then on. Problem solved since 1978.
Ok, start shooting on Ada. It is hardened in almost 30 years of solid development. Best language choice if integrity counts.
Karel Thönissen on April 19, 2007 8:15 AM;-)
\'; DROP TABLE users; -- on April 19, 2007 8:57 AM19 deadly sins is a good overview because it's small and easy to digest, but go read "The Art of Software Security Assessment" if you want a real eye-opener. It's pretty thick (~1200 pages), but they dig a lot deeper into the whole range of vulnerabilities, particularly for C/C++. "Secure Coding in C/C++" is another good one too. It's pretty short and it ties into the work being done at CERT (http://www.cert.org/secure-coding/). And there's always the classic "Writing Secure Code," but it's getting a bit dated.
Kris on April 19, 2007 9:15 AMI see a lot of faults due to the programmer failing to validate user data (or other data). Can you really blame the language of choice for laziness?
-N
NRR on April 19, 2007 9:19 AMI think there should be a knowledge area about software security in the upcoming version of the Software Engineering Body of Knowledge (www.SWEBOK.org), since most of today's systems and applications are more vulnerable to different threats than before, and the impact can be disastrous.
Brian on April 19, 2007 10:01 AM> Any insecurities in the OS itself that would affect a C/C++ program will also affect a C# program, plus you have the additional "leaky abstraction" of the .NET Framework.
And as Thompson's ACM article points out, a compiler's a leaky abstraction too. I still fail to see how you're introducing a new class of potential breaks with a managed framework. A security flaw could be discovered in the Java compiler or in gcc or in the Java framework or in static libraries you're linking against.
It just sucks a bit harder when there's a .Net security patch because they're wont to be way bigger files than an updated compiler.
@April 19, 2007 08:57 AM
I had to laugh at that one...
Dean on April 19, 2007 10:26 AM> I see a lot of faults due to the programmer failing to validate user data (or other data).
Failing to validate user data is a huge problem. It's the root cause of:
- Command Injection
- SQL Injection
- Cross-Site Scripting (XSS)
- Format String
And part of Improper File Access, too.
Developers need to design software with the realization that *some of their users will be evil*, and design accordingly. You can't trust user input, ever.
Jeff Atwood on April 19, 2007 10:26 AMInteresting article from Schneier that touches on the usability issues of security:
http://www.wired.com/politics/security/commentary/securitymatters/2007/04/securitymatters_0419?currentPage=all
--
Of course, it's more expensive to make an actually secure USB drive. Good security design takes time, and necessarily means limiting functionality. Good security testing takes even more time, especially if the product is any good. This means the less-secure product will be cheaper, sooner to market and have more features. In this market, the more-secure USB drive is going to lose out.
I see this kind of thing happening over and over in computer security. In the late 1980s and early 1990s, there were more than a hundred competing firewall products. The few that "won" weren't the most secure firewalls; they were the ones that were easy to set up, easy to use and didn't annoy users too much. Because buyers couldn't base their buying decision on the relative security merits, they based them on these other criteria. The intrusion detection system, or IDS, market evolved the same way, and before that the antivirus market. The few products that succeeded weren't the most secure, because buyers couldn't tell the difference.
--
I actually picked this book up a couple months ago for some light night reading. It is really nice the way that they breakdown the Sins.
Brandon K. on April 19, 2007 11:40 AMThe only secure computer is one that is not plugged in and (in the case of laptops) not turned on.
rabid wolverine on April 19, 2007 1:48 PMYeah, it sure is a good read. I misplaced my copy during some relocation and been searching for it since then :(
KNOCKS on April 19, 2007 1:55 PMMine was stolen, some pages were taken and replaced with erroneous information, then it was put back in my briefcase.
Red on April 19, 2007 4:49 PMVia Ned Batchelder:
The security announcement site XSSed has an archive of identified XSS vunerabilities, ordered by the traffic the page receives: TOP Pagerank List. The listings include an iframe demonstrating the vulnerability. Very slick. It's sobering to see how many high-profile sites have problems like this.
Jeff Atwood on May 29, 2007 10:55 PMSANS identifies the three programming errors most frequently responsible for critical security vulnerabilities:
http://www.sans-ssi.org/top_three.pdf
1. Accepting input from users without validating and sanitizing the input
2. Allowing data placed in buffers to exceed the buffer
3. Handling integers incorrectly
Jeff Atwood on May 31, 2007 9:34 AMAnyways, it would be interesting to see a framework developed that would not let 'net developers do dangerous things quite so easily.
http://stroybalans.ru/
I'm a network and firewall administrator and here's my complains about application people.
- They don't know the port their application is using.
- Some know the port number but can't tell if its TCP or UDP.
- They are RUDE and always want to have their way. The STUPID way of neglecting security over connectivity by asking firewall administrator to open all ports.
Application people either support a finished software or involved on its development. Whichever, they should understand the importance of security which start from the software development and know their products.
BBW on May 19, 2009 6:45 AM| Content (c) 2009 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved. |