Adam Innes · Blog

OWASP Top 10 2013: What Changed and One Habit for Each Risk

· 7 min · security, owasp, web development

OWASP released the final version of its Top 10 for 2013 last Wednesday, June 12. If you build web applications, this is the list your security team, your auditors and probably your next client’s questionnaire are likely to point at, so it’s worth knowing what’s in it. The good news is that it wasn’t shuffled for the sake of it. If you know the 2010 edition, most of the new one will look familiar, and the few changes say something useful about where real applications keep going wrong.

What the list is and how OWASP built it

The OWASP Top 10 project exists to raise awareness. The 2013 document says its goal is to name some of the most critical application security risks organizations face, and developers are first in the list of people it wants to educate, ahead of designers, architects and managers. It isn’t a complete checklist, and it says so plainly: there are hundreds of other issues, and it sends you to OWASP’s other guides for those. This release also marks ten years of the project. The first list came out in 2003, got minor updates in 2004 and 2007, and the 2010 edition was reworked to rank items by risk rather than by how common they are alone. The 2013 edition keeps that approach.

As for how the ranking was made, OWASP says the 2013 list rests on vulnerability prevalence data contributed by application security firms, a mix of consulting companies and tool vendors, covering more than 500,000 vulnerabilities across hundreds of organizations and thousands of applications. The items were picked and ordered using that prevalence data together with consensus estimates of how exploitable, how detectable and how damaging each kind of flaw tends to be. Each entry carries ratings for those factors, based on OWASP’s Risk Rating Methodology, but the document deliberately leaves the threat agents and the business impact up to you, since only you know your application and your business. I think that’s the right way to read the whole thing. The order is a reasonable default for a broad set of organizations, not a verdict on your app.

What changed since 2010

The release notes in the document spell out five changes, and they’re easy to follow once you line the two lists up.

First, Broken Authentication and Session Management moved up, trading places with Cross-Site Scripting, so it’s now A2 and XSS is A3. OWASP’s own guess is that people are looking at authentication harder, not that the flaws have become more common. Second, Cross-Site Request Forgery dropped from A5 to A8. OWASP credits six years on the list, with organizations and framework developers paying enough attention to cut the number of real CSRF bugs significantly. That’s a nice reminder that framework defaults actually move the needle.

Third, the 2010 category Failure to Restrict URL Access was broadened into A7, Missing Function Level Access Control. The reasoning is simple: a URL is only one of the ways a request can say which function it wants, so the category now covers access checks on functions in general. Fourth, the 2010 categories Insecure Cryptographic Storage and Insufficient Transport Layer Protection were merged into A6, Sensitive Data Exposure, which also takes in sensitive data risks on the browser side. The new category follows sensitive data from the moment a user hands it over, through transmission and storage, and back out to the browser. Fifth, there’s a genuinely new entry at A9. The 2010 list mentioned vulnerable components inside Security Misconfiguration, and in 2013 they get a category of their own, because OWASP says the growth of component based development has raised the risk considerably. The release notes call it using known vulnerable components, while the list itself names it Using Components with Known Vulnerabilities. Same thing.

The rest is bookkeeping. Security Misconfiguration moves from A6 to A5 to fill the gap CSRF left, and Injection (A1), Insecure Direct Object References (A4) and Unvalidated Redirects and Forwards (A10) stay where they were.

One habit for each risk

The document has prevention advice for every category, and it’s worth reading in full. What follows is my own take on a single habit per risk that fits into everyday development. It helps to picture where each one lives as a request moves through your app.

Keep data out of your commands (A1)

Injection happens when input ends up being read as part of a query or command. The habit is to never build SQL, shell commands or similar strings by gluing user input into them. Use bind parameters every time, even when the value “can’t” be anything but a number. In Java 7 with JDBC that looks like this, using a PreparedStatement and try-with-resources:

String sql = "SELECT id, email FROM customers WHERE customer_id = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
    stmt.setString(1, customerId);
    try (ResultSet rs = stmt.executeQuery()) {
        while (rs.next()) {
            String email = rs.getString("email");
        }
    }
}

The same rule applies inside an ORM. OWASP specifically warns that a framework’s query language can still be injectable if you concatenate strings into it, so use its parameter binding too.

Don’t hand roll authentication and sessions (A2)

Custom login and session code is where the subtle mistakes pile up. The habit here is to lean on the session management your platform already provides and then check a few specific behaviors: the session ID changes when someone logs in, it never appears in a URL, logout really invalidates it on the server, and idle sessions expire. OWASP’s advice for organizations points the same way, toward one strong, simple set of authentication and session controls that every developer uses.

Escape for the place the data lands (A3)

XSS is about untrusted data becoming active content in the browser. The habit is to escape output at the point where you write it, for the context it’s going into, because HTML body text, an attribute value, a script block and a URL each need different treatment. Template engines that escape by default make this far easier. OWASP also suggests considering Content Security Policy as a site wide extra layer, which I’d treat as a backstop rather than the fix.

Check authorization on the server, for every request (A4 and A7)

These two are close cousins. A4 is about a user changing an ID and getting someone else’s record, and A7 is about a user reaching a function their role shouldn’t have. The habit for both is the same: authorization happens on the server, every time, in one consistent place. When you load a record by ID, confirm the current user is allowed that exact record, for example by including the owner in the query. When a request hits a function, check the role there, with access denied unless it’s explicitly granted. Hiding a button in the UI doesn’t protect anything, and OWASP says as much.

Treat configuration and dependencies like code (A5 and A9)

Both are about what your code runs on. For configuration, the habit is to script how environments are built so development, testing and production are set up the same way (with different passwords), and to make sure users never see stack traces or chatty error pages. For components, keep a list of every library and framework you ship, including the dependencies they pull in, and watch for security announcements about them. OWASP notes that most projects fix vulnerabilities in new versions rather than patching old ones, so staying reasonably current is the real defense.

Decide what’s sensitive, then protect it end to end (A6)

Start by deciding which data actually needs extra protection, like passwords, card numbers and health or personal records. Then the habit is simple to say: don’t keep it unless you need it, encrypt it in transit and at rest, and store passwords with an algorithm designed for passwords. OWASP names bcrypt, PBKDF2 and scrypt. A plain fast hash isn’t in that group. The browser side counts too, so pages showing sensitive data shouldn’t be cached, and sensitive form fields shouldn’t autocomplete.

Put a token on every state changing request (A8)

CSRF works because the browser attaches cookies to forged requests automatically. The habit is an unpredictable token, unique at least per session, in a hidden field on every form that changes something, verified on the server. Remember that OWASP’s explanation for CSRF dropping down the list is that organizations and framework developers focused on it, so if your framework has this protection built in, the job is mostly making sure it’s switched on and not bypassed for the odd endpoint.

Don’t let parameters choose where users go (A10)

Redirects that take a destination URL from a request parameter are handy for phishers. The habit is to avoid them where you can, and where you can’t, accept a short key that the server maps to a known destination instead of a URL, or check the value against a whitelist.

The takeaway

The 2013 list is an evolution, not a rewrite. Two changes deserve your attention: sensitive data is now treated as one problem from the form field to the database and back, and your third party components are officially part of your attack surface. Read the full document, compare it with the 2010 edition if your team still quotes the old numbering, and pick the one or two habits above that your codebase is weakest on.

← all posts