Secure Development - Injection Flaws

Feb 04 2010

Welcome to the second post in my series on secure development issues. This week's topic is injection flaws, including SQL and command injection. The most common types of web application injection flaws include:

  1. Database systems: SQL injection (e.g. 1=1)
  2. Script languages such as Perl, Python, JavaScript
  3. Shells for external commands (e.g. ; rm -rf /)
  4. Calls to the operating system via system calls
  5. Path traversal in file names (e.g. ../../etc/passwd)

Let's look at an example of a typical SQL injection attack: Let's say a developer writes the following Java code to build an SQL query to authenticate users.

var query = "SELECT * FROM users WHERE user = '" + username + "' AND password = '" + pwdHash + "'";

If an attacker enters the following into the input form, he can bypass authentication completely:

User Name: admin' OR 1 = 1; --
Password: ihackstuff

As a result, the following query gets executed on the database:

SELECT * FROM users WHERE user = 'admin' OR 1 = 1; --' AND password = '359f83f8dc9b4ada5ea4d18c31cc212d'

Since the 1 = 1 condition is always true, and everything after the double dash -- is treated as a comment, this will almost always authenticate the attacker and (as a bonus) return a list of all valid users in the table.

The underlying problem that causes injection flaws is a failure to separate statement structure from input parameters. Since SQL statements (and most other interpreted languages) can be easily represented as strings, developers are tempted to simply concatenate strings to create them. This opens the door for the mixing of statement structure and user-controlled input parameters, giving the attacker control over the structure.

Developers can use these techniques to avoid opening their code up to injection flaws:

  1. Avoid external interpreters wherever possible, use language-specific libraries instead: for example, don't use Runtime.exec("mail ..."), send mail via the JavaMail API.
  2. Use proper input validation on all external inputs (from users, other systems, etc.); refer to last week's post on this topic for more details.
  3. Specific to SQL, use parameterized queries which explicitly separate query structure from its parameters. In JDBC, this is implemented by the PreparedStatements class.
  4. If you must call out to an external interpreter, use the following precautions to limit the risk of injection attacks:
    • Encode values before sending to backends: single quotes in SQL statements, commas/parentheses in LDAP statements, etc.
    • Run the external application with limited privileges
    • Check all output, return codes, and error codes and take appropriate action (more on that in my post on error handling, in about two weeks)

In summary, injection flaws can often be very subtle and hard to detect. Separating structure from parameters, relying on native libraries, and using good input validation helps protect against these attacks.

Next week, we'll talk about Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF), which are somewhat related but serve two different purposes to an attacker.

About the Author

Daniel is a business and technical systems analyst with a background in IT security and software development. He has four years of experience in the IT security field, including published academic research. His main areas of expertise include secure development, network security, and authentication. In addition to security, Daniel has a software development background in languages such as Java, PHP, SQL, and Perl. He also has over 12 years experience working with and administering various versions of Linux and related open-source software.

 

Disclaimer

The words and opinions expressed here are those of each article's respective author, and do not necessarily represent the views of CapTech Ventures.