I have some grim news for the programmers in the audience. This news comes with a silver lining of course.
If you are a web developer (or are thinking about teaching yourself web programming), you probably don't think of yourself as a security engineer, or a white-hat/blue-team member of an information security assurance team. You might have considered security threats in the context of quality assurance before (e.g. validating input), but perhaps you're no expert on the subject.
But the second your code is deployed in production, your code is the front line of defense for that entire system and quite possibly the entire network. Logically, that means the software you produce must be made reasonably secure.
Accidentally concatenate a user-provided value with your query string, and someone can steal all your data and cripple your application in their wake. Forget to escape some data before displaying it, or escape it improperly, and suddenly your application has betrayed your users' trust in your client's brand and infected them with malware.
This might seem like a lot of pressure. A common argument I hear from other developers is, "Our clients don't pay us for security; they want it pretty, they want it feature-complete, and most importantly they want it done yesterday."
I'm not going to say you need to become an application security expert. That very notion betrays the (largely untapped) potential for rich diversity in the technology communities. But I will say this:
Application Security is Every Developer's Responsibility
This doesn't mean you have to be an expert. You can take one step forward on the path towards expertise and stop, and it will still move the needle in your organization as well as any clients you work with. As long as you take the right first step.
There are way more than 10 or 25 vulnerabilities. I used to joke that someone was going to hack the planet with OWASP's number eleven vulnerability because no one would see it coming.
Attackers don't think in checklists; skilled attackers think in graphs. "What is the easiest way to get from outside this system to a privileged position inside of it?" If you're trying to build an application and you don't want people to steal your data, infect your customers, and abuse your network resources for distributed denial of service attacks, a checklist mentality isn't going to help here.
Henceforth, I propose a new model for thinking of security weaknesses, which is more approachable for application security neophytes and lends more towards developing a mentality that nurtures the development of layered defenses.
A Taxonomic Model for Insecurity
Instead of creating a checklist of arbitrary size (OWASP Top 10, SANS Top 25, Paragon Top 50, whatever), we should classify security vulnerabilities like we do with living beings. Like organisms, many security vulnerabilities appear to have features and traits in common. Like life, the complexity and diversity of possibilities can seem incomprehensible, but at a very high level, we can place them in a mere handful of categories. And that is what I'd like to share with you today.
What follows are my picks for the a high level taxonomic rank for security vulnerabilities. If you're a new developer and you can learn the lessons of each category, you will be much better off than someone who only knows a domain-specific defense strategy and not the fundamental problem.
1. Failure to Separate Data from Instructions
This covers the majority of security vulnerabilities exploited by hackers of any persuasion, and covers specific topics including:
- Affects database queries. By sending a specially crafted string, you can change the query that the application runs. In many cases, you can abuse this to upload malware.
- Affects Active Directory, similar to SQL Injection. LDAP Injection can result in arbitrary reading/writing of data from the directory server, which can result in privilege escalation of user accounts in an entire network.
- Affects XML document querying. Can result in arbitrary data disclosure from the remainder of the document.
- Affects shell executions.
In all of these cases, the end user (or another device) is able to enter some data into the system, whereby it manages to alter the instructions intended to operate on said data.
2. Unsound Application Logic
Application logic is considered less exciting for many security researchers than turning data into instructions, but logic errors are often overlooked in a rush to take care of the vulnerabilities that can lead to the webserver being taken over.
Unsound application logic occurs when invalid data is blindly accepted without validation, steps can be skipped entirely, or the outcome of the previous step in a sequence is not verified before continuing on.
Application logic errors are best illustrated by example.
Let's say you're designing an eCommerce website that uses PayPal for credit card processing. Without knowing much about checkout integration, the most obvious and straightforward process looks like this:
- User adds items to their cart.
- User presses PayPal button.
- User is taken to the PayPal website.
- Some magic happens.
- User clicks a special link (static, not unique per user) which marks the contents of their cart as
paidand redirects them to a thank you page.
- User sees thank you page.
There are three possible logic errors that can take place here, provided you don't explicitly work to prevent them:
- Users can add -1 of a different item with the same price to their cart to bring the total down to $0.00.
- Users can skip the checkout process entirely by knowing the special link (Step 5) beforehand, which marks the transaction as fulfilled.
- User can add a cheap item to the cart, click the PayPal link, go add a bunch of expensive items to their cart, finish their PayPal purchase (Step 4) with the lower total, and then the whole shebang is marked as fulfilled.
The first case is remedied with simple input validation.
In the latter two cases, server-server API integration is the only real solution. Instead of relying on the user to click a URL (which is brittle even in a utopian world where no one acts maliciously), your checkout provider will tell your server which items were purchased and how much money was transmitted.
All of the failures in my example can be detected by manual human oversight (which doesn't scale to high transaction volumes) but why create more work for your client? One of the benefits of software is the capability for automation, but that automation is only as reliable as our systems can be secure.
3. Your Application's Operating Environment
Your application does not exist in a pocket dimension. It depends on a number of other components to function:
- Third-party libraries
- Your server's operating system
- Your server's hardware components
- The hypervisor, if you're in a virtual machine
- The power supply to the data center
- Network connectivity to the outside world
- Your end user's client software (e.g. web browser)
Security vulnerabilities in any of these components can weaken or outright cripple the security of your application. Generally, the only way to be safe is to keep all of your software up to date and never rely on abandonware.
A related example: Resource asymmetry. Most websites run on a single machine and will fall over if hammered with thousands of machines. This is called a Distributed Denial of Service attack. The only effective mitigations are at the network level, not the application layer.
4. Cryptographic Weaknesses
Although many cryptography-related issues can fall under "operating environment" or "application logic", they really do deserve special consideration.
For example, outside the scope of cryptography, comparing two strings isn't a big deal. In cryptography, you have to be sure that string comparison takes the same amount of time whether your first byte is the same in both strings or if it differs. Otherwise, your application becomes vulnerable to remote timing attacks.
And that's but one of the many things that can go catastrophically wrong when writing cryptography features.
Cryptography is hard. Leave it to the experts.
It's fine to experiment with cryptography in order to learn, but don't ever deploy it in production or recommend anyone use it.
Miscellaneous Security Weaknesses
It's certainly possible that there are broad categories of security issues that I am not aware of. It's also likely that there are security issues that don't fit neatly into one of the four categories I've outlined above. Or perhaps some that fit into two or more categories equally.
Specific example: Are XXE processing vulnerabilities really an instruction/data issue when its only mitigation is turning off a libxml feature? I can't say.
Towards a More Secure Tomorrow
I believe this model, at a 10,000 foot view, is a simpler and gentler way to introduce application security concepts than expecting developers to memorize an arbitrarily sized list of the most common security vulnerabilities from a given year.
It's not perfect; in the end, the security community will have to decide what the categories should be, where the lines should be drawn, and how many layers it should have. Everyone is invited to join in the effort to refine this model.
But it's a start, and if you can remember these four rules, you'll be in good shape:
- Prevent data from corrupting the instructions that operate on it.
- Be explicit and comprehensive with your application logic.
- Keep your software up to date and don't rely on abandoned components.
- Don't write your own cryptography.
If, after reading all of this, you still want to look at the OWASP/SANS checklists for the most common issues (or just to pique your curiosity), feel free. Though I don't consider them good starting points for beginners, they remain excellent resources. You can find many more resources at our application security reading list on Github.
We're not the only ones talking about programmers and new ways to approach software security. If the above model doesn't help any, some of these blogs (or specific blog posts) might be a better fit.