Writing secure applications from the ground up requires a programmer to fully understand all the features he uses to protect his code from vulnerabilities. Today's languages provide many ways to ease coders with hardening their code. You can rely on built-in libraries to filter input variables, escape the output in HTML etc.
However, staying secure is not that simple - you need to know all the limits and caveats of your tools, because sometimes they promise you something but do not deliver - as it is in case of magic quotes functionality.
Magic quotes
PHP
magic_quotes_gpc are sometimes recommended as a protection from
SQL injection .When this ini setting is on, all GET/POST/COOKIE variables are automatically run through
addslashes() function, basically escaping all quote characters with "
\".
All is good
Let's look at an example, where that protection would work:
<?php
// some admin login functionality
$logged_in = mysql_query("SELECT 1 FROM users WHERE login='admin' AND password=sha1('" . $_POST['password'] . "')");
// process $logged_in
When
magic_quotes_gpc are ON injecting the PASSWORD variable won't work. A simplest attack vector "
' OR 1=1 -- " would transform to
SELECT 1 FROM users WHERE login='admin' AND password=sha1('\' OR 1=1 -- ')
That's a perfectly escaped SQL query, nothing can get injected.
magic_quotes_gpc escape the dangerous single quote for us. So - goodbye SQL injection?
WRONG!
Magic quotes only insert a backslash before a few characters, nothing more. That protects you from SQL injection
only in some particular cases like above and only by coincidence. To prove that, let's analyze the following script.
So much for magic...
<?php
// display a single post based on id
$post_data = mysql_query("SELECT title, content FROM posts WHERE post_id={$_GET['id']}");
// display $post
The same "
' OR 1=1 -- " transforms to invalid query:
SELECT title, content FROM posts WHERE post_id= \' OR 1 = 1 --
and if you have display_errors ON, you just made the attacker very happy with your database details outputted right before his eyes. Even if you don't, there still is a possiblity of
blind sql injection.
But what if an attacker set id parameter to one of these:
- 1 AND (SELECT COUNT(*) from another table_name) BETWEEN 0 AND 100
- -10000 union select user_password from users where user_login=CHAR(97,100,109,105,110) (admin)
You won't get any protection from these vectors - they slip right under the eyes of
magic_quotes_gpc - they don't contain quotes.
In this case a single mysql query parameter that was left unprotected could lead to your whole database content dumped to the attacker (I'm not kidding - see
sqlmap and try for yourself).
Know your context
The fundamental problem with
magic_quotes_gpc is that they know nothing about
the context. They don't know if you're using the data to insert it into MySQL, Oracle, or if you're writing to a file. Maybe you're sending it through SOAP or displaying it in HTML? Or maybe all of it. They just don't have enough information, only you know it. Escaping values depends on a context in which they are used.
You should disable magic quotes now also for the following reasons:
- they mangle your data before you get the chance to reach it and can lead to double escaping,
- most frameworks expect them to be off, and if they're enabled, try to undo these changes at bootstrap,
- they're deprecated in PHP 5.3 and will be removed in PHP 6.
But you need to escape your data nonetheless. Follow these simple rules:
- to output to a HTML - use htmlspecialchars()
- to escape a string in MySQL - use mysql_real_escape_string().
- to escape a string in Postgresql - use pg_escape_string()
- When you are expecting an integer in SQL query - simply cast to int
- ALWAYS use prepared statements when querying databases.
If you want to read more on bypassing magic quotes and SQL injection in general, I recommend:
Have any comments on the subject to share? Please do so, this is my first security-related post and I'd like to hear your opinion.