CP363 : SQL Security

SQL has security issues beyond the typical user and permissions already discussed. It is critically important that any front end to a SQL database be secure. This normally means preventing users from committing SQL injection attacks against a database. In a SQL injection attack a malicious user 'injects' injurious SQL code into a SQL statement. These attacks can be particularly subtle, but are fairly easily defeated.

SQL Injection

Injection attacks are successful when input is not properly filtered. Imagine a simple user authentication system that checks to see whether a call to a user table returns at least one record. The Python code that defines the statement looks like:

      
sql = f"SELECT * FROM users WHERE uid = '{uid}'"

The uid variable comes from a simple web form. Imagine that the attacker enters the following value in the uid field of the form:

      
whatever' OR 'x'='x

    

The resulting SQL statement as generated by Python is:

      
SELECT * FROM users WHERE uid = 'whatever' OR 'x'='x'

    

Since 'x' is always equal to 'x' this would dump the entire contents of the user table.

Most database servers allow multiple SQL statements to be executed when they are separated by ';'. This can also be used in a SQL injection attack. Using the same Python code as before the attacker enters:

      
whatever'; DELETE * FROM users; SET x = 'x

    

The resulting SQL statement(s) as generated by Python is:

      
SELECT * FROM users WHERE uid = 'whatever';
DELETE * FROM users;
SET x = 'x'

    

The attacker is not logged in, but the entire contents of the 'users' table could be deleted.

If data types are not checked then similar attacks can be made. The Python code:

      
sql = f"SELECT * FROM broad WHERE broadId = {id}"
    

clearly expects id to be an integer variable. Instead, the attacker enters:

      
0; DROP TABLE broad;

    

creating the SQL statement:

      
SELECT * FROM broad WHERE broadId = 0;
DROP TABLE broad;

    

potentially destroying the 'broad' table.


Defeating SQL Injection

SQL injection attacks can be easily defeated by parameterizing any input that is passed to a SQL statement. Parameterizing is automatically done by statement preparation. Thus the first Python example would be written as:

      
sql = "SELECT * FROM users WHERE uid = %(uid)s"
params = {'uid': uid}
cursor.execute(sql, params)

    

The %(uid)s is a placeholder, and the params variable is a dictionary that contains the values that replace the placeholder(s). The execute method filters, escapes, and quotes the replacement data for proper use in the prepared statement. Thus the attempted attack input:

      
whatever' OR 'x'='x

    

becomes just a string that happens to contain escaped (i.e. \') single quote characters:

      
SELECT * FROM users WHERE uid = 'whatever\' OR \'x\'=\'x'

    

which executes with errors, but returns an empty result set.

The attempt to drop the table becomes:

      
SELECT * FROM users WHERE uid = '0; DROP TABLE broad;'

    

with the uid parameter being just a simple single-quoted string.

Note that different languages, drivers, APIs, and extensions provide different methods for filtering and escaping input, but almost all provide some kind of parameterizing specifically to deal with SQl Injection.


Now you will understand why this cartoon is funny.