The CWE Top 25 Programming Mistakes
I’ve read some debate about the top 25 programming mistakes as documented by the CWE (Common Weakness Enumeration) project, in collaboration with the SANS Institute and the MITRE . That the list isn’t complete, that there are some items that aren’t in the list, but should be, or vice-versa.
I think we should look at the CWE top-25 as something like the PCI Data Security Standard – it’s not the be-all and end-all of security, it’s not universally applicable, it’s not even a “gold standard”. It’s just the very bare minimum that you should be paying attention to, if you’ve got nowhere else to start in securing your application.
As noted by the SANS Institute, the top 25 list will allow schools and colleges to more confidently teach secure development as a part of their classes.
I personally would like to see a more rigorous taxonomy, although in this field, it’s really hard to do that, because in large part it’s a field that feeds off publicity – and you just can’t get publicity when you use phrases like “rigorous taxonomy”. Here’s my take on the top 25 mistakes, in the order presented:
Insecure Interaction Between Components
“These weaknesses are related to insecure ways in which data is sent and received between separate components, modules, programs, processes, threads, or systems.”
- CWE-20: Improper Input Validation
- What’s proper input validation? Consider the thought that there is no input, no output, only throughput. A string is received at the browser, and turned into a byte encoding; this byte encoding is sent to the web server, and possibly re-encoded, before being held in storage, or passed to a processing unit. For every input, there is an output, even if it’s only to local in-memory storage.
- Validating the input portion falls broadly into two categories – validating for length, and validating for content. Validating for length seems simple – is it longer than the output medium is expecting? You should, however, check your assumptions about an encoding – sometimes encodings will add, and sometimes they will remove, counts of the members of the sequence – and sometimes they may do both.
- Validating for content can similarly be broken into two groups – validating for correctness against the encoding expected, and then validating for content as to “business logic” (have you supplied a telephone number with a square-root sign or an apostrophe in it, say). Decide whether to strip invalid codes, or simply to reject the entire transaction. Usually, it is best (safest) to reject the entire transaction.
- CWE-116: Improper Encoding or Escaping of Output
- The other part of “throughput validation” – and while we constantly tell programmers that they should refuse to trust input, that should not be held as an excuse to produce untrustworthy output. There are many times when your code is trusted to produce good quality output. Some examples:
- When you write a web application visited by a user, that user trusts you not to forward other people’s code on to them. Just your own, and that of your business partners. [See Cross-Site Scripting, below]
- When your application is used internally [See SQL Injection, below]
- Be conservative in what you send – make sure it rigorously follows whatever protocol or design-time contract has been agreed to. And above all, when sending data that isn’t code, make sure to encode it so that it can’t be read as code!
- CWE-89: Failure to Preserve SQL Query Structure (aka 'SQL Injection')
- SQL Injection is a throughput validation issue. In its essence, it involves an attacker who feeds SQL command codes into an interface, and that interface passes them on to a SQL database server.
- This is almost an inexcusable error, as it is relatively easy to fix. The fix is usually hampered somewhat in that the SQL database server is required to trust the web server interface code, but that means only that the web server interface code must either encode, or remove, elements of the data that is being passed in the SQL command sequence being sent to the server. The most reliable way to do this is to use parameterised queries or stored procedures. Avoid building SQL commands through concatenation at almost any cost.
- CWE-79: Failure to Preserve Web Page Structure (aka 'Cross-site Scripting')
- I hate the term “cross-site scripting”. It’s far easier to understand if you just call it “HTML injection”. Like SQL injection, it’s about an attacker injecting HTML code into a web page (or other HTML page) by including it as data, in such a way that it is provided to the user as code.
- Again, a throughput content validation issue, anything that came in as data and needs to go out as a part of an HTML page should be HTML encoded, ideally so that only the alphanumerics are unencoded.
- CWE-78: Failure to Preserve OS Command Structure (aka 'OS Command Injection')
- Like SQL injection, this is about generating code and including data. Don’t use your data as part of the generation of code.
- There are many ways to fix this kind of an issue – my favourite is to save the data to a file, and make the code read the file. Don’t derive the name or location of the file from the user-supplied data.
- CWE-319: Cleartext Transmission of Sensitive Information
- What’s sensitive information? You decide, based on an analysis of the data you hold, and a reading of appropriate laws and contractual regulations. For example, with PCI DSS, sensitive information would include the credit card number, magnetic track data, and personal information included with that data. Depending on your state, personal contact information is generally sensitive, and you may also decide that certain business information is also sensitive.
- Seriously, SSL and IPsec are not significant performance drains – if your system is already so overburdened that it cannot handle the overhead of encrypting sensitive data, you are ALREADY too slow, and only providence has saved you from problems.
- Especially where the data is not your own, make an informed decision as to whether you will be communicating in clear text.
- CWE-352: Cross-Site Request Forgery (CSRF)
- Another confusing term, CSRF refers to the ability of one web page to send you HTML code that your browser will execute against another web page. This really is cross-site, and forges requests that look to come from the user, but really come from a web page being viewed in the user’s browser.
- The fix for this is that every time you display a form (or even a solitary button, if that button’s effects should be unforgeable), you should include a hidden value that contains a random number. Then, when the “submit” (or equivalent) button is pressed, this hidden value will be sent back with the other contents of the form. Your server must, of course, validate this number is correct, and must not allow the number to be long-lived, or be used a second time. A simple fix, but one that you have to apply to each form.
- This really falls under a category of guaranteeing that you are talking to the user (or the user’s trusted agent), and not someone pretending to be the user. Related to non-repudiation.
- CWE-362: Race Condition
- Race conditions refer to any situation in which the execution of two parallel threads or processes behaves differently when the order of execution is altered. If I tell my wife and son to go get a bowl and some flour, and to pour the flour into the bowl, there’s going to be a mess if my wife doesn’t get the bowl as quickly as my son gets the flour. Similarly, programs are full of occasions where a precedence is expected or assumed by the designer or programmer, but where that precedence is not guaranteed by the system.
- There are books written on the topic of thread synchronisation and resource locking, so I won’t attempt to address fixing this class of issues.
- CWE-209: Error Message Information Leak
- Be helpful, but not too helpful. Give the user enough information to fix his side of the error, but not so much that he has the ability to learn sensitive information from the error message.
- “Incorrect user name or password” is so much better than “Incorrect password for that user name”.
- “Internal error, please call technical support, or wait a few minutes and try again” is better than “Buffer length exceeded at line 543 in file c:\dev\web\creditapp\cardcruncher.c”
- Internal information like that should be logged in a file that is accessible to you when fixing your system, but not accessible to the general end users.
Risky Resource Management
“The weaknesses in this category are related to ways in which software does not properly manage the creation, usage, transfer, or destruction of important system resources.”
- CWE-119: Failure to Constrain Operations within the Bounds of a Memory Buffer
- The old “buffer overflow” – a throughput length validation issue. Any time you take data from one source and place it into another destination, you have to reliably predict whether the destination is large enough to hold it, and you also have to decide what you will do if it is not.
- Don’t rely solely on .NET or Java “protecting you from buffer overruns” – when you try and access an element outside of a buffer’s limits, they will simply throw an exception – crashing your program dead in its tracks. This in itself could cause half-complete files or other communications, which could feed into and damage other processes. [And simply catching all exceptions and continuing blindly is something I’ve complained about before]
- CWE-642: External Control of Critical State Data
- By “Critical State Data”, this refers to information about where in the processing your user is. The obvious example of bad external control of critical state data is sending the price to the user, and then reading it back from the user. It obviously isn’t too hard from an attacker to simply modify the value before sending it to the server.
- Other examples of poorly chosen state being passed includes the use of customer ID numbers in URLs, in such a way that it is obvious how to select a different customer’s number.
- State data such as this should generally be held at the server, and a ‘reference’ value exchanged to allow the server to regain state when a user responds. If this value is populated among users sufficiently sparsely, it’s close to impossible for an attacker to steal someone else’s state.
- CWE-73: External Control of File Name or Path
- This is related to forced-browsing, path-traversal, and other attacks. The idea is that any time you have external paths (such as URLs) with a direct 1:1 relationship to internal paths (directories and paths), it is usually possible to pass path control from the external representation into the internal representation.
- Make sure that all files requested can only come from a known set of files; disable path representations (such as “..”, for ‘parent directory’) that your code doesn’t actually make use of.
- Instead of trying to parse the strings yourself to guess what file name the operating system will use, always use the operating system to tell you what file name it’s going to access. Where possible, open the file and then query the handle to see what file it really represents.
- CWE-426: Untrusted Search Path
- Windows’ LoadLibrary is the classic example of this flaw in design – although the implicit inclusion of the current directory in Windows’ execution PATH searched is another.
- When writing programs, you can only trust the code that you load or call if you can verify where you are loading or calling it from.
- A favourite trick at college was to place ‘.’ at the front of your path, add a malicious shell file called ‘rm’, and invite a system administrator to show you how to kill a print job. The “lprm” command he’d run would call “rm”, and would run the local version, rather than the real command. Bingo, instant credentials!
- Don’t search for code that you trust – know where it is, and if it isn’t there, fail.
- CWE-94: Failure to Control Generation of Code (aka 'Code Injection')
- I find it hard to imagine the situation that makes it safe to generate code in any way based off user input.
- Perhaps you could argue that this is what you do when you generate HTML that contains, as part of its display, user input. OK then, the answer here is to properly encode that which you embed, so that the code processor cannot become confused as to what is code and what is data.
- CWE-494: Download of Code Without Integrity Check
- Either review the code that you download, or insist that it is digitally signed by a party with whom you have contracted for that purpose. Otherwise you don’t know what you are downloading or what you are executing.
- CWE-404: Improper Resource Shutdown or Release
- This covers a large range of issues:
- Don’t “double-free” resources. Make sure you meticulously enforce one free / delete for every allocation you make. Otherwise, you wind up releasing a resource that you wanted to hang onto, or you may crash your program.
- If the memory you’re about to release (or file you’re about to close) contained sensitive information, make sure it is wiped before release. Verify in the release build that the optimiser hasn’t optimised away this wiping!
- Make sure you release resources when they are no longer in use, so that there are no memory leaks or other resource overuse problems that will lead to your application becoming bloated and fragile.
- CWE-665: Improper Initialization
- Define all variables’ types – no “IMPLICIT INTEGER*4 (I-N)” (Am I showing my age?)
- Put something into your variables, so that you know what’s there. Don’t rely on the compiler unless the compiler is documented to guarantee initialisation.
- By “variable”, I mean anything that might act like a variable – stretches of memory, file contents, etc.
- CWE-682: Incorrect Calculation
- Again, a multitude of sins:
- “should have used sin, but we actually used cos”
- divide by zero – or some similar operation – that causes the program to halt
- length validation / numeric overflow – in a single byte, 128 + 128 = 0
- As you can see, a denial of service can definitely occur, as can remote execution (usually a result of calculating too short a buffer, as a result of numeric overflow, and then overflowing the buffer itself)
- Don’t underestimate the possible results of just plain getting the answer wrong – cryptographic implementations have been brought to their knees (and resulted in approving untrustworthy access) because they couldn’t add up properly.
“The weaknesses in this category are related to defensive techniques that are often misused, abused, or just plain ignored.”
- CWE-285: Improper Access Control (Authorization)
- This one pretty much speaks for itself. There’s public parts of your application, and there’s non-public parts. Make sure that you have to provide authentication before crossing that boundary, and make sure that the user account verified in authentication is the one that’s used for authorisation to access resources.
- Carry user authentication information around carefully, without letting it be exposed to other forms of attack, but also to make sure that the information is available the next time you need to authorise access to resources.
- CWE-327: Use of a Broken or Risky Cryptographic Algorithm
- Translation – get a crypto expert to manage your crypto. [Note – this is why I recommend using CryptoAPI rather than OpenSSL, because you have to be your own expert to use OpenSSL.]
- New algorithms arise, and old ones become obsolete. In the case of cryptographic algorithms, obsolete means “no longer effectively cryptographic”. In other words, if you use an old algorithm, or a broken algorithm, or don’t use an existing algorithm the right way, your data isn’t as protected as you thought it was.
- Where possible, use a cryptographic framework such as SSL, where the choice of cryptographic algorithms available can be adjusted over time to deal with changing realities.
- CWE-259: Hard-Coded Password
- If there’s a hard-coded password, it will be discovered. And when discovered, it will be disseminated, and then you have to figure out how to get the message out to all of your users that they can now be owned because of your application. Not an easy conversation to have, at a guess.
- This is a “just don’t do it” recommendation, not a “do it this way” or “do it that way”.
- CWE-732: Insecure Permission Assignment for Critical Resource
- If a low-privilege user can lock, or corrupt, a resource that is required for high-importance transactions, you’ve created an easy denial-of-service.
- If a low-privilege user can modify something that is used as a basis for trust assignments, there’s an elevation of privilege attack.
- And if a low-privilege user can write to your code base, you’re owned.
- CWE-330: Use of Insufficiently Random Values
- Give me a random number. 7. Give me another random number. 7. And another? 7.
- How do you tell if a number is random enough? You hire a mathematician to do a statistical analysis to see if the next number is predictable if you know any or all of the previous numbers.
- This mostly ties into CWE-327, don’t do your own crypto if you’re not a crypto expert (and by the way, you’re not a crypto expert). However, if you’re hosting a poker web site, it’s pretty important to be able to shuffle cards in an unpredictable manner!
- Remember that the recent Kaminsky DNS attack, as well as the MD5 collision issues, could have been avoided entirely by the use of unpredictable numbers.
- CWE-250: Execution with Unnecessary Privileges
- Define “unnecessary”? No, define “necessary”. That which is required to do the job. Start your development and testing process as a restricted user. When you run into a function that fails because of lack of privileges, ask yourself “is this because I need this privilege, or can I continue without?”
- Too many applications have been written that ask for “All” access to a file, when they only need “Read”.
- Too many applications demand administrator access when they don’t really need it. I’m talking to you, Sansa Media Converter.
- CWE-602: Client-Side Enforcement of Server-Side Security
- I’ve seen this one hundreds of times. “We prompt the user for their birth date, and we reject invalid day numbers”; “Where do you reject those?”; “In the user interface so it’s nice and quick”. Great, so I can go in and make a copy of your web page, delete the checks, and input any number I like. Don’t consider it impossible that an attacker has written his own copy of the web browser, or can interfere with the information passing through the network.
Glaringly absent, as usual, is any mention of logging or auditing.
Protections will fail, always, or they will be evaded. When this happens, it’s vital to have some idea of what might have happened – that’s impossible if you’re not logging information, if your logs are wiped over, or if you simply can’t trust the information in your logs.
Maybe I say this because my own “2ndAuth” tool is designed to add useful auditing around shared accounts that are traditionally untraceable – or maybe it’s the other way around, that I wrote 2ndAuth, because I couldn’t deal with the fact that shared accounts are essentially unaudited without it?
Of course, that leads to other subtleties – the logs should not provide interesting information to an attacker, for instance, and you can achieve this either by secreting them away (which makes them less handy), or by limiting the information in the logs (which makes them less useful).
Another missing issue is that of writing software to serve the user (all users) – and not to frustrate the attacker. [Some software reverses the two, frustrating the user and serving the attacker.] We developers are all trained to write code that does stuff – we don’t tend to get a lot of instruction on how to write code that doesn’t do stuff.
Another mistake, though it isn’t a coding mistake as such, is the absence of code review. You really can’t find all issues with code review alone, or with code analysis tools alone, or with testing alone, or with penetration testing alone, etc. You have to do as many of them as you can afford, and if you can’t afford enough to protect your application, perhaps there are other applications you’d be better off producing.
Other mistakes that I’d like to face head-on? Trusting the ‘silver bullet’ promises of languages and frameworks that protect you; releasing prototypes as production, or using prototype languages (hello, Perl, PHP!) to develop production software; feature creep; design by coding (the design is whatever you can get the code to do); undocumented deployment; fear/lack of dead code removal (“someone might be using that”); deploy first, secure later; lack of security training.