[thelist] php/mysql and form input...sorta

Simon Willison simon at incutio.com
Fri Aug 16 04:41:01 CDT 2002


At 01:00 16/08/2002 -0700, Roger Harness wrote:
>I *think* I'm possibly having problems with htmlspecialchars(),
>stripslashes(), and addslashes()...or maybe not.
>
>I'm just trying to let the user select a plu number, which will then bring
>up a form with that plu's info already filled out in appropriate text boxes,
>with each field waiting to be edited if needed. I'm just trying to
>semi-reuse my working code for inputting the plu's, and all its
>corresponding data fields.
>
>mysql_select_db("roger");
>$query = "select * from feeders3 where plu = '$searchterm' order by descr";
>$result = mysql_query($query);
>$num_results = mysql_num_rows($result);
>echo "<p>Number of products found: ".$num_results."</p>\n";
>for ($i=0; $i <$num_results; $i++)
>{
>$row=mysql_fetch_array($result);
>echo htmlspecialchars( stripslashes($row["descr"]));
>echo "<input type=text value=".stripslashes($row["style"]).'>';

You shouldn't have to stripslashes() on data that has come out of a
database. Here's why.

The whole point of addslashes/stripslashes is to get quotes in to a
database without breaking things. Consider the following SQL command:

insert into tablename set name = 'Bob', age = 21

Nothing wrong with that. Now consider this statement:

insert into tablename set name = 'Bob's Car', age = 7

This statement will fail - the apostrophe in Bob's Car breaks out of the
string early. The solution is to use addslashes() on the variable
containing "Bob's Car" before the data goes in the database, which will add
a slash in front of the hyphen and escape the data in a way understood by
mySQL / other database engines.

You should never need to run stripslashes() on data that has come out of a
database. The slashes are only there to get the insert / update SQL calls
to work - the data is stored in the database without slashes and will be
returned without slashes as well.

Here's where things get confusing. PHP has a setting called "magic quotes"
which automatically runs the addslashes() function on any data coming in to
the script. This is a two edged sword - it means you don't have to worry
about running addslashes() on your data, but it also means that if you DO
the data will end up "double slashed" (a term I just made up). Double
slashed data will be stored in the database in a way that requires you to
run stripslashes() on the data when it comes back out of the database. The
problem is that while magic quotes defaults to being on it can be turned
off in the php.ini file, and some servers do not have it enabled. As a
result, for rock solid PHP code that wioll work on all servers whether or
not they have magic quotes turned on you need to use this on all data
coming in:

$data = get_magic_quotes_gpc() ? $_REQUEST['data'] :
addslashes($_REQUEST['data']);

The above line will guarantee that the incoming data (in a field from a
form named 'data') has addslashes() applied once to it, whether or not the
server has magic quotes enabled.

Still with me? Good, because there's one more thing to consider. If magic
qutoes is turned on and you want to just display a piece of data straight
to the user you will need to run stripslashes() on it first to get rid of
the slashes. Imagine they've submitted a form with "Bob's Car" as the
setting for the "data" field, and magic quotes is turned on. Using

echo $_REQUEST['data'];

Will output "Bob\'s Car" - PHP has added slashes for you because it thought
you were going to put the data in a database. TO display the variable
without the additional slashes you need:

echo stripslashes($_REQUEST['data']);

Of course, if magic quotes isn't turned on the above could cause problems
(by removing slashes that are meant to be there for example) - so you end
up having to use this to make sure:

echo get_magic_quotes_gpc() ? stripslashes($_REQUEST['data']) :
$_REQUEST['data'];

One last problem: You may think that with magic quotes being such a royal
pain in the butt it makes sense to disable them or enable them specifically
at the start of a script using a call to the set_ini() function. This seems
like a great idea, until you realise that changes to PHP's ini
configuration in this manner are global rather than local to the currently
executing script. In other words, if you change the magic quotes ini
section at the top of your script any other scripts on the same server that
start executing during the execution of your script will be affected. This
can lead to all kinds of bizzare problems with slashes cropping up in weird
places in unrelated scripts. I'm not sure if this is confirmed as a problem
with magic quotes and the set_ini function but I seem to remember reading
about it somewhere and it would explain some strange bugs I have seen on
our company's server.

The best solution is to have a piece of code at the top of your script
which goes through all input and runs addslashes() on it if magic quotes is
turned off (or runs stripslashes if magic qutoes is on and you want it to
be off - it's your call). To complicate things more, this code needs to be
able to handle arrays within your incoming input. I had a look around and
couldn't find any code for doing this that didn't use eval() so I put the
following together:

/* Recursive functions that deal with the array problem */
function addslashes_recursive($value) {
     if (is_array($value)) {
         foreach ($value as $index => $val) {
             $value[$index] = addslashes_recursive($val);
         }
         return $value;
     } else {
         return addslashes($value);
     }
}
function stripslashes_recursive($value) {
     if (is_array($value)) {
         foreach ($value as $index => $val) {
             $value[$index] = stripslashes_recursive($val);
         }
         return $value;
     } else {
         return stripslashes($value);
     }
}

/* Use this if you want addslashes() to be run on all incoming data */
if (!get_magic_quotes_gpc()) {
     // Recursively apply addslashes() to all data
     $_GET = addslashes_recursive($_GET);
     $_POST = addslashes_recursive($_POST);
     $_COOKIE = addslashes_recursive($_COOKIE);
     $_REQUEST = addslashes_recursive($_REQUEST);
}

/* Use this if you do NOT want addslashes() to be run on all incoming data */
if (get_magic_quotes_gpc()) {
     // Recursively apply stripslashes() to all data
     $_GET = stripslashes_recursive($_GET);
     $_POST = stripslashes_recursive($_POST);
     $_COOKIE = stripslashes_recursive($_COOKIE);
     $_REQUEST = stripslashes_recursive($_REQUEST);
}

Hope that helps, and thanks for finally giving me the inspiration to write
a bit of code I've needed for ages :)

Simon Willison
http://www.bath.ac.uk/~cs1spw/blog/




More information about the thelist mailing list