§9.5.

Defensive coding

Cryptography alone does not solve the “CIA triad” of confidentiality, integrity and availability. SSL/TLS may stop attackers from intercepting connections. However, it doesn’t protect against attackers advantage of bugs in code.

As developers, it is our responsibility to write secure code. Writing secure code requires a security mindset that combines both paranoia and creativity. Paranoia, because you need to expect the very worst from malicious users in every line of code you write. Creativity, because you need to be imagining unexpected abuses of your system, long before any attackers dream up those attacks themselves.

Security is difficult. Fortunately, there are many good guides to developing secure websites. I recommend the Open Web Application Security Project (OWASP). Among their many guides and instructional recommendations, is their regular “Top Ten” effort. The OWASP Top 10 is a consensus of security professionals of the ten most important threats to web application security. The current top 10 is as follows:

Exercise: Common risks

Spend at least 30 minutes reading the summary of the OWASP Top Ten and the one-page reviews linked from the top ten.

Which risks do you believe might apply to your projects? Which ones are you confident do not apply?

As you read through the risks, pay attention to how an understanding of cryptography helps you understand security principles.

In the remainder of this section, I will discuss the most important of the OWASP top 10: injection.

Injection

Injection can occur in code that unnecessarily trusts user-supplied data.

Suppose we have an application that uses SQL to query a database. The following query is a representation of what might occur when ‘mike’ attempts to log in.

async function checkMike() {
    let sql = `select id, name from accounts
               where username = 'mike'
               and password   = 'asdf1234'`;

    return await pool.query(sql);
}

Of course, we do not hard-code a method for each user. Instead, the username and password are parameters supplied from user input. A naïve version of the login code might look like the following:

// WARNING: This code has problems!
async function checkLogin(username, password) {
    let sql = `select id, name from accounts
               where username = '${ username }'
               and password   = '${ password }'`;

    return await pool.query(sql);
}

The above code will work, but it has a problem.

Exercise: Identify the risk

Before continuing, can you see the issue?

Read the OWASP guide to injection if you’re not sure.

The issue is that the user can ‘inject’ SQL into the query.

For example, what happens if the user logs in with username mike and password ' or '1' = '1?

Take the query:

    let sql = `select id, name from accounts
               where username = '${ username }'
               and password   = '${ password }'`;

Replacing username and password, we would have:

    let sql = `select id, name from accounts
               where username = '${ "mike" " }'
               and password   = '${ "' or '1' = '1'" }'`;

The value of sql would be the following:

select id, name from accounts
where username = 'mike'
and password   = '' or '1' = '1'

Notice the or in the last line? '1' = '1' is always true! This means that the system will always log in (because anything or '1' = '1' is always true).

Allowing users to log in without a valid password is a huge security issue.

Most database query libraries offer parameterized queries to solve this problem. Instead of building queries out of strings, a parameterized query allows you to write a query and supply the user input as separate parameters or values.

async function checkLogin(username, password) {
    let sql = `select id, name from accounts
               where username = $1
               and password   = $2`;

    return await pool.query({
        text: sql,
        values: [username, password]
    });
}

In this code, the database interprets the username and password as values. These values will not be decoded or executed as SQL code.

Reflection: Injection in MongoDB

MongoDB queries are JavaScript objects rather than strings. Is it possible for an attacker to inject code into a login query on MongoDB? If so, how? How do you defend against it?

Tip
It is possible. It is easy to write insecure code in MongoDB when your API accepts JSON request bodies. You may need to search the web to find out how.
Exercise: Types of injection

The big list of naughty strings contains strings that can create problems for many web applications.

Read through the list in the file blns.txt.

How many of these strings are potential injection attacks in a web application? What configuration problems would the injection strings reveal?