|
|
[SQL Injection]Bil Corry, December 01, 2004 SQL injection is the security concern where an improperly formed sql inline may allow someone to "inject" their own SQL queries into your sql inline; by doing so, they may be able to insert, modify, retrieve or delete data. An improperly-formed sql inline is one where raw user-provided data is directly plugged into a sql inline without validation. This includes hidden inputs in forms! The rule-of-thumb is all data coming from the user must be treated as hostile until validated. For this article, we're going to examine the five most common injection points in sql inlines and how to secure them:
Strings
Did you get that? Let's look at an example, it'll become more clear. Here is a sql query that plugs in the user-provided value for searching on a first name: var:'sql'=" Imagine name_first = "Bil" - the sql query becomes this: SELECT name_first, name_last FROM contacts WHERE name_first='Bil' So far, so good, right? Now imagine instead the user entered: crackerboy'; delete from contacts; # Now the query becomes: SELECT name_first, name_last Viola! Your entire contacts table is hosed. (The pound sign # tells MySQL that the rest of the line is a comment). So how do you stop 'crackerboy' from sql injection in strings? You need to escape all the quotes. Fortunately, Lasso has a built-in tag to do it for you, [encode_sql] Here's our original query rewritten to use it: var:'sql'=" So properly encoded, now the query becomes: SELECT name_first, name_last That slash \ in front of the embedded quote tells MySQL to ignore it, and thus, the entire string becomes the name_first search criteria. So that's how you protect your strings in queries, but what about numeric values? Numeric ValuesUnfortunately, numeric values can not be protected using encode_sql, reason being you wouldn't (normally) have quotes around the value to begin with, so you wouldn't need to escape any quotes being sent. But I'm getting ahead of myself. Let's take a look at a query that uses a numeric value: var:'sql'=" Imagine userid = "123" - the sql query becomes this: SELECT name_first, name_last FROM contacts WHERE userid = 123 Already this has a problem, the user could feed sequential numbers to your query and pull out the entire database list. But that's another topic for another time. Now imagine instead the user entered: 123; delete from contacts; Now the query becomes: SELECT name_first, name_last Viola! Your entire contacts table is hosed. (Notice the theme here?) And even if we encode_sql the passed-in value, it wouldn't change anything. So how do you prevent crackerboy from hosing your table this time? Make sure that you explicitly pass a numeric if that's what your query is expecting. While you could validate the passed-in value to ensure it's numeric using something like [string_isdigit], I instead just cast it explicitly to a numeric using [integer] and [decimal] (depending on if the query needs an integer or decimal). So let's rewrite that query to prevent sql injection for integer: var:'sql'=" So what happens, you ask, if the user provides the following? 123; delete from contacts; Well, when Lasso converts a string to an integer or decimal, it takes every digit it can until it hits a non-numeric character. So casting to integer, the query would become: SELECT name_first, name_last FROM contacts WHERE userid = 123 Of course, decimals are handled in an identical fashion. Make that your homework assignment. Date ValuesDates in MySQL can either be strings or numeric, MySQL accepts either. But to prevent SQL injection, the trick is to cast the passed-in value to a Lasso date type using [date] and formating the output for MySQL. I won't go into how you convert a string to a date type as it depends on your implementation, but I will show you how to create a sql query using a Lasso date type: var:'sql'=" Since a Lasso date type can not hold anything other than dates, you are assured that any bogus strings will be rejected when trying to cast it to a Lasso date. LIKE queriesLIKE queries are really just strings, but with an extra twist of pattern matching. What that means is LIKE queries will recognize special characters within the string as wildcard charaters, namely, % and _. You can read more about what % and _ do in a pattern-matching query on the MySQL website. To show how % and _ can be abused, let's start off with an example query: var:'sql'=" This query will allow someone to enter the first letter(s) of a last name, and find all contacts that begin with them. So imagine the user entered "cor" (you require and validate that they enter at least three letters), the query would look like: SELECT name_first, name_last FROM contacts WHERE name_last LIKE 'cor%' But imagine if the user entered "%%%", the query would become: SELECT name_first, name_last FROM contacts WHERE name_last LIKE '%%%%' which would find all of your contacts! So how to handle LIKE? You have two options. Either delete all % and _ found in the string or escape them by replacing % with \% and _ with \_. Be sure to still encode_sql the string. Note that you only escape % and _ for LIKE queries (or any of the other pattern-matching queries that recognize % and _), don't go doing it on all your query strings. REGEXP queriesREGEXP queries is another pattern matching query that has a whole slew of special characters. Unless you want your users passing their own regular expressions, most likely you'll just want to encode their value to be used in a regular expression you designed. The trick is to precede every character in the string with double backslashes \\, so a passed-value of "1+2" becomes "\\1\\+\\2". (Side note: You have to do the same thing for Lasso's regexp tags if you're feeding them user data)
|
Articles |