Today, I’m going to give away some source code! Celebrate! I wrote the code to address a relatively common problem among new programmers: the over-reliance on Magic Quotes.
Do you know what Magic Quotes are? It’s the annoying feature in PHP that goes around randomly (okay, not so random, per se) modifying your data to protect you from yourself. If you have it turned on and someone types in crap in your web form, well… Let me show you.
Original:
I hate magic quotes because it’s awesome at screwing up otherwise good content. And if you’re unlucky and decide to edit your text, it tends to add even more backslashes into your pretty content. Thus, stuff like ‘\’ becomes \’\\\’.
Freak version after I decide to edit the content:
I hate magic quotes because it\’s awesome at screwing up otherwise good content. And if you\’re unlucky and decide to edit your text, it tends to add even more backslashes into your pretty content. Thus, stuff like \’\\\’ becomes \\’\\\\\\’.
Awesome, huh? Do you see that freak-show at the end there? That’s right: if you forget to strip backslashes out of your content before you let people edit stuff they’ve saved, it can get progressively worse, adding in backslashes like your mother added veggies to your meal when you were a kid. It’s that bad. And it’s very common.
WHY?
Of course, the logic for the feature is obvious. The designers of PHP decided that it was better for the content to get jacked up than for millions of developers everywhere getting fired for letting 15 year old hackers run “DROP DATABASE” in their corporate servers (for those of you who don’t know what I mean, that command equates to Armageddon on your servers).
But I still hate it. It smells of noobishness, and it encourages sloppy coding. Having to strip slashes out of your code is not the way you should be doing things. There are four reasons I argue against magic quotes.
- If you are having to use magic quotes, you’re already committing tons of SQL sin.
- Not all servers you work with will have magic quotes on by default. Programming for security should be a defensive practice, and, thus, programmers should be trained to assume the least secure environment.
- Magic quotes alone won’t protect you from SQL inject attacks. Character encoding can be used to pass in un-escaped single quotes.
- This feature will be gone in PHP 6.
I’m not going to go too much into #1 because that list is way too long to cover. In short, you should be using prepared statements to minimize SQL injection, and using a single, unified database abstraction class to handle all your querying to centralize security weaknesses.
The second point is important. You can’t be a great body guard if you assume nothing is ever suspicious. Same goes for being a programmer writing secure SQL. Of course, the whole point of magic quotes is to prevent the body guard from forgetting to check one of the closets, even though he checked every other room in the 1000 room house. People forget, and that’s unfortunate. Magic quotes is that paranoid body guard that goes around handcuffing anything that moves, and it’s your job to go around and free each person. It’s the guy that walks around nailing every door shut and insists everybody lives in a bullet proof glass box. There has to be a better way, and there is.
The third point is the one novices rarely know. You can pass in invalid characters into a query that then get converted, thanks to Mr addslashes, into a single quote. This relates to the inconsistency of converting single-byte characters into multi-byte characters. As the article quoted here mentions:
Whenever a multi-byte character ends in 0×5c (a backslash), an attacker can inject the beginning byte(s) of that character just prior to a single quote, and addslashes() will complete the character rather than escape the single quote. In essence, the backslash gets absorbed, and the single quote is successfully injected.
Yeah, it’s gibberish to me too. The point is, converting certain hex values in certain foreign languages screws up and can leave you with a hanging single quote. Magic quotes don’t save you from that.
Lastly, PHP 6 isn’t going to have magic quotes on by default. Might as well take off those training wheels now.
The Fix
I have two solutions for you.
The first is to write a database abstraction layer. What? A database abstraction “layer” is a fancy word for a class that manages your database connection and data manipulation. I gave a brief example of how to write one of these a while back. Another is to create a function (or method, if you’re talking about classes) that does INSERT and UPDATE querying for you. Such a function’s prototype would look like this:
function perform($tableName, $data, $whereClause)
The $tableName variable is a string, such as “user”. The $data variable is an array where each key is a column name and each value is the value to be assigned to that column. This data is sanitized (addslashes and what not) before being inserted. The $whereClause variable is used as a suffix to an UPDATE query (ex. “WHERE user_id = 1”). This method commonly exists in many open source projects.
The problem with this method is that you still have to escape variables manually for the WHERE clause. And it doesn’t even cover how to do SELECT statements safely.
So I sat here thinking for a bit about this, trying to think of a simple, elegant solution to this problem that would help PHP beginners everywhere. Before I hand out my solution for free, let’s go over the main points:
- SQL injection attacks (vulnerabilities) come from putting user provided input directly into SQL queries. User input comes from $_POST, $_GET, and sometimes $_COOKIE.
- Data that is passed around by the developer that was retrieved from the database is mostly safe since it has already been sanitized (if sanitization happened before it was loaded in).
- Data from files, XML, or other forms of potential stream input need to be sanitized as well. But this would be done manually.
The Class
That said, I wrote a class that solves the main point. With it, all POST, GET, request, and cookie data can be accessed through a nice clean abstraction layer. The goal is that if you’re using this, you’d avoid using un-sanitized data, unless you meant to. For example, to access the $_GET[‘name’], $_POST[‘name’], $_REQUEST[‘name’], or $_COOKIE[‘name’] variables, you’d call:
$safe = new DbSafe();
// O’Reilly becomes O\’Reilly
$name = $safe->get(‘name’);
$name = $safe->post(‘name’);
$name = $safe->request(‘name’);
$name = $safe->cookie(‘name’);
If you wanted to get the original unmodified values, you’d call:
$safe = new DbSafe();
// O’Reilly is still O’Reilly
$name = $safe->get(‘name’, TRUE); // notice the second parameter
$name = $safe->post(‘name’, TRUE);
$name = $safe->request(‘name’, TRUE);
$name = $safe->cookie(‘name’, TRUE);
That even takes into consideration whether or not magic quotes are on. In other words, if magic quotes are on and your variables are getting slashed up, the code I show above would spit out the original version that was typed in by the user. What good is my library if it didn’t do some auto-detection, eh? =)
If you wanted to escape a value manually, you’d say:
$safe = new DbSafe();
// It’s becomes It\’s
$escapedValue = $safe->escape($value);
Or…
// It’s becomes It\’s
$escapedValue = DbSafe::escape($value);
If you wanted to escape an entire array, you’d say:
$safe = new DbSafe();
$escapedArray = $safe->escapeArray($array);
Or
$escapedValue = DbSafe::escapeArray($value);
All of these examples would convert a string (or an array of strings) that said:
Hello, my name’s Michi
To:
Hello, my name\’s Michi
When you saved this into the database, that little backslash disappears so next time you read it, it looks like this:
Hello, my name’s Michi
No need to strip anything! If you want to directly access the values without stupid slashes being automatically added in (“magically,” if you will), my class supports that as a secondary measure.
Is this class the end-all-be-all for secure programming? No. Really, the better solution that I won’t give away today is to write a strong database abstraction layer. But this will do most of your dirty work without requiring magic quotes, and without making developers think PHP has some kind of built in “security.” Remember, you can’t always rely on magic quotes being on, nor should you.
You can get the source here.
Thanks for pointing this out, Maha. This has been fixed.
The src link (http://www.michiknows.com/code/DbSafe/DbSafe.php) gives a 404. Thought you’d like to know
I hate it when things hinder me while forcibly trying to help me. Yes, I am talking about the goddamn magic quotes. I don’t need no help with escaping quotes, if I am incompetent I deserve to bear the consequence. And I understand it’s the responsibility of the programmer to take care of user input.
Two things I absolutely hate about PHP:
i). Magic quotes
ii). Consistent function/method names