Thursday, May 4, 2017

Injection

A1: Injection

SQL injection is the most common type of injection attack, and Grails applications are largely immune to these, but not entirely. An SQL injection attack typically consists of tricking the application into running SQL queries or updates that either damage data or expose information. This can happen when you have a search form or other web page that accepts user input and you use the input as part of a dynamically generated SQL query without properly escaping the inputs.
Using regular JDBC, you can use a java.sql.Statement to run a select query; for example:
String sql = "select * from person where username ='" + params.username + "'"
ResultSet rs = statement.executeQuery(sql)
This works well if you have control over the inputs, but users can enter whatever they want in your form. If someone enters foo, then the where clause of your query will be where username ='foo', but if a hacker enters ' or '1'='1, then it will be select * from person where username ='' or '1'='1'. Because '1'='1' is always true, the or results in the query returning unexpected records (in this case, all of them). Tricks like this can be used to bypass password checks during login or create a denial-of-service style attack where too much data is returned from the database repeatedly, or even to damage data or tables. If you use execute instead of executeQuery, you can mix select queries and updates and allow real damage:
boolean ok = statement.execute(sql)
If a hacker submits '; drop table foo; -- or '; truncate table foo; --, you’ll be scrambling to restore the database from the most recent backup.
The problem here is that we’re trusting the users to do the right thing. The deeper problem is a failure to escape the user input properly before sending it to the database. You could look for patterns like the ones I’ve shown and implement a whitelist/blacklist filtering approach to using user-submitted data in your queries, but the best approach is to let the database driver do the work for you. Rather than using a Statement, use a PreparedStatement with parameter placeholders in the SQL:
String sql = "select * from person where username = ?"
PreparedStatement ps = connection.prepareStatement(sql)
ps.setString(1, params.username)
ResultSet rs = ps.executeQuery()
Now, if an unfriendly user submits a username with quote characters, they will be escaped properly (the approach is different for various databases, but the driver handles it for us) and the worst-case scenario now is an SQLException.
Fortunately for us, Hibernate uses a PreparedStatement for criteria queries, and all Grails queries are converted to criteria queries under the hood (the exception being single-element queries like get() or read(), which also use a PreparedStatement). You can see this by turning on SQL logging and enabling SQL comments in DataSource.groovy:
dataSource {
   ...
   logSql = true
}
hibernate {
   ...
   format_sql = true
   use_sql_comments = true
}
Given this simple domain class:
class Person {
   String username
}
You can use a few different approaches to find a user by username:
Person.findByUsername(params.username)

Person.where { username == params.username }.find()

Person.createCriteria().get {
   eq 'username', params.username
}
and each of these results in roughly the same SQL:
Hibernate:
    /* criteria query */ select
        this_.id as id0_0_,
        this_.version as version0_0_,
        this_.username as username0_0_
    from
        person this_
    where
        this_.username=?
You can see from the comment that Hibernate generated the SQL from a criteria query and, from the SQL, that a PreparedStatement is being used because the username parameter isn’t the actual string being queried, but the ? placeholder.
So we’re safe from SQL injection attacks in the general case, but we can also use HQL queries with the executeQuery and executeUpdatemethods. Hibernate converts our HQL to SQL, so naive string concatentation of HQL can open up an SQL injection vulnerability:
Person.executeQuery("from Person where username='" + params.username + "'")
Hibernate has no way of knowing that a parameter should be escaped, because it just sees the final concatenated string. But, of course, HQL has the same support for placeholder replacement as SQL:
Person.executeQuery('from Person where username=?', [params.username])
and also has support for more readable named parameters:
Person.executeQuery('from Person where username=:username',
                    [username: params.username])
So, as long as you use the standard GORM methods to run your queries and are careful with HQL queries, you should be safe from SQL injection risks. Note that Groovy GStrings don’t help here and, in fact, hide the problem to a certain extent. I could have written the SQL above as "from Person where username='${params.username}'" and the HQL as "select * from person where username ='${params.username}'"; the lack of + characters in the code can make it more likely that this would get missed in a code review.

Command injection

Groovy makes it easy to execute arbitrary operating system commands by adding the execute method to the metaclass of the String and String[] classes. For example, it’s simple to get a directory listing on a Unix or Linux system by running 'ls -l'.execute().text. If your application uses this feature and creates the commands to be executed based on user input, you are at risk of a command injection attack. Unfortunately, there isn’t a simple fix like there is for SQL; you will have to be vigilant and scan the user input based on a whitelist and/or a blacklist of allowed characters and expressions that are valid.

No comments:

Post a Comment

Remote Hybrid and Office work