Magic Quotes
Published in PHP Architect on 21 Mar 2005As frequent readers of Security Corner already know, you should always filter input and escape output. These are key components of a secure application’s foundation, and a good software design focuses on making these tasks easy for developers.
The major problem with the magic quotes directives isn’t just that they usurp a developer’s control (they do), but also (and especially) that they take the wrong approach — they escape input. Remember, you should filter input and escape output, not escape input. The manual page for magic quotes states:
Magic Quotes is a process that automagically escapes incoming data to the PHP script. It’s preferred to code with magic quotes off and to instead escape the data at runtime, as needed.
Why is this so bad?
In the May 2004 edition of Security Corner, I wrote about data filtering with a specific focus on filtering input. A best practice concerning data filtering is to take a whitelist approach — consider all data to be invalid unless it can be proven valid. While not always practical, this is the safest approach.
When data is escaped before it is filtered, the filtering logic becomes more complex, and unnecessary complexity is never a best practice. When the topic is security, complexity breeds mistakes, and mistakes in data filtering logic represent a security weakness at the very least and, most likely, a vulnerability.
I refer to this complexity as unnecessary because the complexity introduced by escaping data before filtering can be removed by simply performing these tasks in the proper order.
Filtering Versus Escaping
Filtering is specific to the type of data you’re dealing with. If you’re attempting to prove the validity of a user’s year of birth, you might require that it be a positive integer that is four digits long and fits within reasonable limits. Thus, filtering is just whatever logic you use to test data and verify your assumptions. This is often a weak point in an inexperienced developer’s implementation, because it’s not very intuitive that you should have to prove your assumptions. This is especially true when you use a select form element or something similar that restricts what casual users can enter.
Escaping
is quite a bit different. This is the process by which you prepare data to be sent
to some remote (external) system — be it the user’s browser, a MySQL database, or
an API. The purpose of escaping data is to keep the data intact. Because
certain characters might be interpreted in special ways by the remote system, these
characters can be escaped so that their original meaning is preserved. Examples
of escaped characters are \'
instead
of '
for MySQL, and <
instead of <
for HTML.
With magic quotes, escaping is performed on input before the first line of code. By the time you get the data, it has already been prepared for a very specific use case — being sent to a database. There are many reasons why this is a poor approach, with three big ones being:
- Escaping input makes filtering it more complex and error prone.
- Escaping for one particular purpose limits what can be done with the data, without the added complexity of restoring the data to its original form.
- The escaping performed by magic quotes is not thorough enough (for any purpose) to be considered a security safeguard.
In Magic Quotes and Add Slashes in PHP Harry Fuecks writes:
Never strip slashes! That’s the golden rule. You should never have to use
stripslashes()
. Ever.
This is a strong point — if you’re having to take steps to restore data to its original form, then you’ve done something terribly wrong.
The PHP manual states:
Magic quotes are implemented in PHP to help [prevent] code written by beginners from being dangerous. Although SQL Injection is still possible with magic quotes on, the risk is reduced.
Reducing risk is what security is all about. However, is the risk of SQL injection really reduced? I think not. I’ve already mentioned the unnecessary complexity that magic quotes add to your filtering logic, but there’s the additional risk that magic quotes can make it more difficult to identify an SQL injection vulnerability. Because magic quotes can hide the obvious problems, such as when an apostrophe in someone’s name causes a database error, they promote a failure to properly escape data. In short, I believe an application is less secure with magic quotes enabled than when they are disabled, and this is independent of all of the other problems, such as portability and performance.
Disabling Magic Quotes
To
disable magic quotes, make sure each of the directives is disabled. In php.ini
, you can do this with the following:
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
If your access to PHP configuration directives is limited to .htaccess
, you can use the following instead:
php_flag magic_quotes_gpc Off
php_flag magic_quotes_runtime Off
php_flag magic_quotes_sybase Off
In some shared-hosting environments, neither of these options may be available to you. I’ve been fortunate enough to have never been involved with any projects that can only afford shared hosting. There are many reasons why you might find yourself developing an application that must be capable of being hosted on a shared server — it’s for a small company with a small budget, it’s an application that you want to distribute widely, or it’s an application for shared hosting providers.
There is a fix_magic_quotes()
function that was written for the NYPHP article on working with databases.
This function takes into account each of the
magic quotes directives and restores all data affected by them. This function
needs to be called before any action (such as filtering) is taken on the data.
And, of course, this should be considered a last resort.
Until Next Time…
I hope I’ve sufficiently convinced you to never develop with any of the magic quotes directives enabled. They are a horrible practice that no PHP developer should participate in. If you thought they were useful for security for some reason, hopefully you now realize that this is nothing more than a misconception.
Until next month, be safe.