I have a form processed with PHP. It contains a field for Notes about the client job.
The problem is that if there is a ' within in the notes - such as it's, O'Reilly, that's etc, it escapes the string in the database, so I have all of the notes up until it encounters the ' then that's the end of the notes.
I realise there are things like mysqli_ and PDO, but it's the busy season at the moment and I could just do with quickly fixing this before doing a complete update/overhaul in January.
Any idea why it isn't working? Code included. It doesn't matter where I put the mysql_real_escape_string(), it doesn't work anywhere.
FYI: The table column is TEXT. And there are a couple of other fields hence the foreach
// SELECTS AND CONNECTS TO SERVER/DB
include_once('config/db.inc.php');
// CONVERT ALL $_POST['name'] to $name and clean/prep for mysql insertion
foreach($_POST as $key => $value ) {
$$key = mysql_real_escape_string($value);
}
// UPDATE CLIENT JOB NOTES
$query = "UPDATE client_list SET bookingNotes='$bookingNotes' WHERE id='$CID'";
mysql_query($query, $conn) or die(mysql_error());
TIA
Edit for the responses below:
$bookingNotes and $CID are define by the form variables $_POST['bookingNotes'] and $_POST['CID'] where the foreach essentially removes the "_POST" part. (that's the whole $$key = $value part)
As mentioned, I appreciate mysqli_ and PDO but am currently unable to learn, update and implement those system wide at the moment. This runs locally and my current version of PHP 5.4.1 supports the function. I understand PDO is better, but for now that is not an option so please don't belittle me with "do it properly" or "learn how to code". That isn't the issue at hand.
I know what's happening and where and why - mysql is treating the ' as and end of the string. But I don't know why it's happening when I believe the function should escape the ' and allow it into the database.
To surmise, this is what happens.
"Today is very grey and it's raining" is entered into the form as $bookingNotes. The script then inputs that into the TEXT column of the database. But what appears in the database is;
"Today is very grey and it"
TIA and Thanks for the responses so far.
Are you sure the problem is on the writing end? Your code looks like it should work. (Well, for some value of work, you don't want to have a field <input name='conn' value='haha'>. Anyway, you seem to understand the problems with the code well enough.)
I assume that what you see is not SQL-injection but HTML injection:
<input name='bookingNotes' value='<?php=$bookingNotes?>'>
try escaping that with
<input name='bookingNotes' value='<?php=htmlspecialchars($bookingNotes, ENT_QUOTES)?>'>
to avoid the problem.
I tried it and it seems okay
$_POST['name'] = "1'";
$_POST['age'] = '2';/*
$_POST[] = '3';
$_POST[] = '3'; */
foreach($_POST as $key => $value ) {
$$key = mysql_real_escape_string($value);
echo $$key;
}
The result I got was : 1\' - which is correct
Thank you to everyone with helpful input.
It's fixed now, I honestly don't know how.
I removed the '' on the SQL statement (WHERE bookingNotes='bookingNotes') to force an error.
I also checked (echoed) the contents of $bookingNotes, which was the POST data AFTER mysql_real_escape_string() and it had the full string with apostrophes escaped (slashed)
I removed the echo, put the SQL back, and it all worked as it should / as expected.
I backed up the script before I started working, I have since compared the "new" working one with the original one that failed and they are exactly the same, no missing characters or extra/missing whitespace, exactly the same. o_O
So I don't know what happened there!
Thanks for the input anyway.
Try changing this
$$key = mysql_real_escape_string($value);
INTO
$key = mysql_real_escape_string($value);
First, mysql_real_escape_string() always works. If some your code doesn't - well, it's your code.
Second, but most important: NEVER run a code like this
foreach($_POST as $key => $value ) {
$$key = // whatever.
}
it is actually a hole in security, worse than any other.
Finally, it is extremely unclear from your extremely vague explanation, but I guess it's HTML form where you get your "end of notes". Anyway, you have to fix the very place where it happens, not blame a function you were running long time ago
Related
I filter the input from user in every submission or i have create function which manipulates the get/post values and call it in every submission of form.
But now i kept the below functions in main include file following:
foreach ($_POST as $key => $value) {
$_POST[$key] = mysql_real_escape_string(trim($value));
}
foreach ($_GET as $key => $value) {
$_GET[$key] = mysql_real_escape_string(trim($value));
}
It works well, but is there any downsides of doing this for EVERY single post/get?
P.S. Well except performance of script.
Validate user inputs rather than escaping first, after validating
and before insertion into db u can escape.
Answer for your question: Doing it in a proper manner is to use
function and place those function in a separate file and including
that file before calling the function.. your furst way is also fine
but if the form and its fields are big in count then the script will
be messy and would be hard to maintain by other developers hence
using the second way of using a file containing your filtering functions is
a good idea in any case....
This isn't a logical way to solve this. As mentioned, this won't handle multidimensional arrays and will be an annoyance if you all of a sudden might need to work with the original value. For instance someone maybe wants to save a textarea field and wants a couple of rows at the bottom to continue the work later on. These rows will vanish upon saving then.
Form validation may get a bit messy aswell.
Your solution isn't a 100% secure method either. You're still vurnerable against XXS.
Apart from that I know no framework which does this automaticly, and they probably aint doing it for a reason.
mysql_real_escape_string does not fully prevent SQL Injection. Lets see the following example:
$input = mysql_real_escape_string($_POST['id']);
$query = "DELETE FROM user WHERE id={$input};";
Lets say $_POST['id'] equal to 1 OR TRUE
I have users entering their name, as in: O'riley.
Before I enter this data into the MySQL DB, I run mysql_real_escape_string.
Problem is, when I then select this data for display and use later, it comes out as: O\'riley.
Obviously, this is the intended operation. What I'm wondering is if there's someway to be sure I can store it in the DB (still safely escaping possible malicious code), so that I don't have to use strip_slashes() on the output EVERY time I call the data throughout the entire web app? Or, am I missing something here?
Thanks.
UPDATE
Please refer to the comments in Deceze's answer.
No, it's not the intended operation to store the string as "O\'riley"; it should only be escaped in the query, but not stored this way. I'll guess that PHP puts in the backslash through Magic Quotes, and you escape it again to make it stick.
Disable Magic Quotes.
I personally always turn off magic quotes because it is doing something I haven't told it to do. If you dont have the ability to turn it off, consider including this code at the top of all of your pages.
if (get_magic_quotes_gpc()) {
function strip_array($var) {
return is_array($var)? array_map("strip_array", $var):stripslashes($var);
}
$_POST = strip_array($_POST);
$_SESSION = strip_array($_SESSION);
$_GET = strip_array($_GET);
}
If you have magic quotes disabled, and in the case that get_magic_quotes_gpc returned 1 and you did something like #Michael shows and this still occurs, magic quotes runtime may be enabled.
Magic quotes runtime will add slashes to strings when doing database queries and even when writing to files. To disable this in the currently executing script, do:
if(function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime())
{
set_magic_quotes_runtime(false);
}
You can also disable this configuration option through php.ini as well, if you can do so. However, if you can't disable it through php.ini permanently, you may need to add # before get_magic_quotes_runtime and set_magic_quotes_runtime as PHP may through E_DEPRECATED errors if error_reporting is set to log such errors (and this will get fairly annoying).
Alright, here's what you need to do. First, based on the comments, from other questions:
ISOLATE
You need to determine what is causing the problem. Is it the server? Is it some hidden code somewhere? What? Some programmers may include code like this on a configuration page:
<?php if (isset($_POST))
foreach ($_POST as $key => $value) $_POST[$key] = addslashes($value);
?>
So here are some checks to see if it is the server. This is about as solid a way of checking as possible. Create a NEW page. Leave it entirely blank and add this code:
<?php print_r($_POST); ?>
<form action="" method="POST">
<input type="text" name="test" value="O'riley" />
<input type="submit" name="submit" value="submit" />
</form>
Save it and load it in the browser. Hit submit. See if the slash is added still.
If it is still adding it, you need to troubleshoot your server / configuration. If it's a server you're paying for, you need to tell them to fix it or shove it. If it's your own server, then you're going to need to do some messing / googling to figure out how to turn off magic quotes.
If there are no quotes showing up, then there is definitely some code adding slashes to your post variables. It might not be as obvious as the code above, however. You need to run a search on your code for "addslashes", "mysql_real_escape_string", and possibly "str_replace". If you find one of these, you need to disable it. But be advised this may break other parts of your site that are assuming this action is taking place. You're other option for storing it in the database is to do a function similar to the code above, but instead, run stripslashes on it. Then you can run mysql_real_escape_string on it later. (A bit more unneeded overhead.)
Use
var_dump(get_magic_quotes_gpc());
somewhere near the actual use of the posted data to see if the Magic Quotes are really off.
Also some weird frameworks may add said quotes for you so grep your code for addslashes and other escape_string-like functions.
I would honestly suggest the use of PDO.
PDO employs the use of prepare and execute statements, which in turn adds security and removes some extra headache.
$pdo = new PDO();
$stm = $pdo->prepare('INSERT... (LastName) VALUES (:LastName)');
$stm->execute(array(':LastName' => "O'Rily"));
$stm->fetchAssoc(PDO::FETCH_ASSOC);
You no longer will need to worry about removing the escaping slashes as well as securing basic sql injection tactics.
It's possible magic quotes are turned on in your httpd.conf or in a virtual host declaration.
The exact location and file will depend on OS and distro. I'd check Apache configuration files for the php_flag setting.
Just use clean the data before inserting in to database,
function check_input($value)
{
if( get_magic_quotes_gpc() )
{
$value = stripslashes( $value );
}
//check if this function exists
if( function_exists( "mysql_real_escape_string" ) )
{
$value = mysql_real_escape_string( $value );
}
//for PHP version < 4.3.0 use addslashes
else
{
$value = addslashes( $value );
}
return $value;
}
$cleandata = check_input($value);
$sql = "INSERT INTO `table_name` SET `field_name`='".$cleandata."'";
While fetching the data and show into it use stripslashes($val)
Thank you everyone for the answers. I will award the +50 out, but I wanted to tell my real solution here, all which people did help with...
I was performing mysql_real_escape_string on all of the data AS SOON as it posted (before any processing). So, a slash was added to escape the ' character that was submitted. This, we know is normal.
However, there was no reason that the backslash \ should show up in the DB entry, right? The escape was there to be sure the ' was entered.
Turns out, AFTER escaping, I would then save the variable to be reloaded to the page in the session, in case the user had an error that PHP found while validating all of the form fields. In this case, the user's input (formerly O'riley was now printed to their screen as O\'riley. Then, the user didn't catch this - so they would often just fix their error that PHP caught during validation (unrelated to the name field), and thus the O\'riley would land in the database because mysql_real_escape_string would escape the characters.
Lesson:
When processing a form, FIRST save data for form-refill use. SECOND validate form fields. THIRD escape the data for processing into the database.
Or better yet, use PDO and avoid this =).
Comments welcome. THANKS ALL!
I'm using $_POST and aware about mysql exploit, I decided to use this function on the top of my page, therefore all POST will be safe:
Can you tell me if I miss something and this function will really do the job as I think it will?
function clean_post(){
if ( $_POST){
foreach ($_POST as $k => $v) {
$_POST[$k]=stripslashes($v);
$_POST[$k]=mysql_real_escape_string($v);
$_POST[$k]=preg_replace('/<.*>/', "", "$v");
}
}
if ( $_COOKIE){
foreach ($_COOKIE as $k => $v) {
$_COOKIE[$k]=stripslashes($v);
$_COOKIE[$k]=mysql_real_escape_string($v);
$_COOKIE[$k]=preg_replace('/<.*>/', "", "$v");
}
}
}
It will also remove all html tag, a safest option to output the result might be to use:
<pre>
$foo
</pre>
Cheers!
Cheers!
I think it's a bad idea to do this. It will corrupt the data your users enter even before it hits the database. This approach will also encourage you to use lazy coding where you consistently don't escape data because you believe that all your data is already "clean". This will come back to bite you one day when you do need to output some unsafe characters and you either forget to escape them or you aren't really sure which function you need to call so you just try something and hope that it works.
To do it properly you should ensure that magic quotes is disabled and only escape data when necessary, using precisely the correct escaping method - no more, no less.
There are some problems with it.
First you apply functions on types that doesn't need them, your integers for example needs only a (int) cast to be secure.
Second you do not secure lenght, when you're requesting a '12 chars string' it would be a good idea to ensure you've got only 12 chars, and not 2048. Limiting size is really something your attackers will not like.
Third in your foreach loop you have a $v variable, you assign 3 times a function on $v to $_POST[$k]. So the 1st two assignements are lost when the 3rd occurs...
Then all the things previous people said are right :-)
The rule is apply the filter at the right moment for the right output. HTML output need an html filter (htmlspecialchars), but the database doesn't need it, it need a database escaping. Let's say you want to extract data from your database to build a CSV or a PDF, HTML escaping will make you life harder. You'll need CSV escaping at this time, or PDF escaping.
Finally it is effectively hard to remember if you are manipulating a data which is already well escaped for your output. And I recommend you an excellent read on Joel on Software about Apps Hungarian. The text is quite long, but very good, and the web escaping sequence is used as an example on why Apps Hungarian is good (even if System Hungarain is bad).
Hi this is my first answer for any question asked on web so please review it.
Put this code in top of your script and no need to assign these posted values to any variables for doing the same job of making the input data safe for database. Just use $_POST values as it is in your query statements.
foreach ($_POST as $k => $v) {
if(!is_array($_POST[$k]) ) { //checks for a checkbox array & so if present do not escape it to protect data from being corrupted.
if (ini_get('magic_quotes_gpc')) {
$v = stripslashes($v);
}
$v = preg_replace('/<.*>/', "", "$v"); //replaces html chars
$_POST[$k]= mysql_real_escape_string(trim($v));
}
}
Don't forget $_GET[]
if ($_POST OR $_GET)
Also you can add strip_tags()
I don't know whether your function is correct or not, but the principle is certainly incorrect. You want to escape only where you need to, i.e. just before you pass things into MySQL (in fact you don't even want to do that, ideally; use bound parameters).
There are plenty of situations where you might want the raw data as passed in over the HTTP request. With your approach, there's no ability to do so.
In general, I don't think it's that good of an idea.
Not all post data necessarily goes into MySQL, so there is no need to escape it if it doesn't. That said, using something like PDO and prepared statements is a better way, mysql_* functions are deprecated.
The regular expression could destroy a lot of potentially valid text. You should worry about things like HTML when outputting, not inputting. Furthermore, use a function like strip_tags or htmlspecilchars to handle this.
stripslashes is only necessary if magic quotes are enabled (which they shouldn't be, but always is possible)
When working with stripslashes I'd use get_magic_quotes_gpc():
if (get_magic_quotes_gpc()) {
$_POST[$k]=stripslashes($v);
}
Otherwise you'll over-strip.
I use this:
function safeClean($n)
{
$n = trim($n);
if(get_magic_quotes_gpc())
{
$n = stripslashes($n);
}
$n = mysql_escape_string($n);
$n = htmlentities($n);
return $n;
}
To prevent any type of MySQL injection or anything like that. Whenever I use it to wrap around $_POST like this:
$username = safeClean($_POST['user']);
$password = md5(safeClean($_POST['password']));
$vpassword = md5(safeClean($_POST['verify']));
$email = safeClean($_POST['email']);
It doesn't even work, but I have attached functions.php and the directory is correct but doesn't work at all because it just shows a blank page... If I remove the safeClean() from each $_POST it works.
How come this isn't working at all?
In my opinion, this sort of general sanitization approach isn't the best way to think about things. For one thing, parameterized queries (probably most convenient using PDO) are a much better way to approach the SQL safety issue. But in general...
I know the developer impulse is to try and reduce the number of things you have to think about. So, naturally, you want to see if you can come up with an all-purpose sanitization function you can just hand all inputs over to and not have to worry any more. Inputs are one arena, though, where if you really want security, you need to think specifically about what each incoming piece of data is supposed to be and where it's going to end up. If you go on auto-pilot here, you will introduce a security issue at some point.
Try using mysql_real_escape_string() rather than mysql_escape_string().
Almost everything in your code is wrong.
get_magic_quotes_gpc is misplaced, htmlentities is misplaced and even term "sanitization" is misused.
As a matter of fact, you shouldn't sanitize anything for the database. But just follow syntax rules.
Take a look at the very similar question, I've explained SQL matters pretty well: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
And as of the blank page, you have to learn primer of debugging. You have to turn error reporting on to see error messages instead of blank page. To start with it you can refer to this article:
Link to start: http://www.ibm.com/developerworks/library/os-debug/
you can start from adding these lines at the top of your cript
ini_set('display_errors',1);
error_reporting(E_ALL);
and this code to the query execution:
$result = mysql_query($query);
if (!$result) trigger_error(mysql_error());
if(get_magic_quotes_gpc())
{
$location_name = trim(mysql_real_escape_string(trim(stripslashes($_GET['location_name']))));
}
else
{
$location_name = trim(mysql_real_escape_string(trim($_GET['location_name'])));
}
That's the code I have so far. seems to me this code is fundamentally ... OK. Do you think I can safely remove the inner trim(). Please try not a spam me with endless version of this, I want to try to learn how to do this better.
UPDATE
So after reading some of the responses, I think I have come to understand a good method for safely getting data from a user, storing it and then displaying it back.
When you first load the page
$foo = trim($_GET['foo']);
if(get_magic_quotes_gpc())
{
$foo = stripslashes($foo);
}
Then when you come to use this variable as part of a SQL string, even if not storing the data in the database, you should escape it.
mysql_real_escape_string($foo);
And finally, if reading data from the database and wanting to display it as HTML, such a post on a blog or forum, you should pass the variable using htmlspecialchars
echo(htmlspecialchars($bar));
Would any one like to suggest a better set of functions to use? other then obviously wrapping these functions to make them simpler to call.
Here:
$location_name = trim($_GET['location_name']);
if(get_magic_quotes_gpc()) $location_name=stripslashes($location_name);
Then there is also the SQL injection protection, but don't do this until the very last moment before sticking this var in an SQL query. And even then don't apply the changes to the var itself, but rather a copy. You might want to show $location_name to the user afterwards (for example if the form fails). So
$sql="UPDATE whatever(location) VALUES('" . mysql_real_escape_string($location_name) . "')"
I'm assuming of course that $location_name will end up in the database; otherwise you don't need mysql_real_escape_string.
Finally you want to use htmlspecialchars if you're going to display $location_name on your page somewhere.
Edit: You want to use htmlspecialchars() just before displaying the data (definately don't save data that has already been transformed via htmlspecialchars in your database). In general you want to use escaping functions at the last moment and then on a copy of your var. That way you know that at any point during the script the var is the original one and is not carrying some random escape characters from a transformation that happened somewhere before.
You also know where your escape functions are/should be. sql escaping is near/at your sql query. XSS escaping (htmlspecialchars) is near the part where you display data in a web page.
Finally once you get the grip of things, you could always forego SQL escaping by using PHP's PDO functions. Also in the future you might want to take at look at this: Do htmlspecialchars and mysql_real_escape_string keep my PHP code safe from injection?
I am sorry to say but everything in your question is wrong.
First, it has nothing to do with performance, by any means. these functions never become a bottleneck and never cause any performance issue.
Next, You've choose wrong place to get rid of magic quotes. Magic quotes is input data related, not database related. It it is better to make a distinct function and place it in your configuration file, being included into every script. You can use one from here
So, the code become like this:
$location_name = mysql_real_escape_string(trim($_GET['location_name']));
But i strongly advise you not to mix database escaping with anything else, as anything else is optional while database escaping is strict and unconditional.