My application is working fine. DB connection has been opened before call SQL commands in PHP.
The problem is that some parameters in an input form is blank, and after using real_escape_string the parameters have an empty string stored in database. The database columns are set to default to NULL.
Is this expected? I can't find anything relevant in PHP documentation.
Is it possible to simply make it store NULL?
Code is as below:
"INSERT INTO address SET firstname = '" . $mysqli->real_escape_string($data['firstname']) . "'";
It’s expected if you tell the server to use the empty string, which you are doing. You need to add some logic to your code to use null when a string is blank.
Also, you are wide open to SQL injection. You need to use prepared statements, rather than concatenating variables into your query. Escaping strings is not enough. See How can I prevent SQL injection in PHP?.
You probably should separate the data verification from the query creation. This can be done as follows:
$firstName = strlen($data['firstname'])? "'".$mysqli->real_escape_string($data['firstname'])."'": "NULL";
$sql = "INSERT INTO address SET firstname = " . $firstName;
This will check that $data['firstname'] has a value in it and if not, Null is used. This then is combined into your query that you then will run in some subsequent step.
This is by no means the only (or even the best) approach, but based on the code that you have provided, this should give you a start.
Related
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 7 years ago.
I am beginning the process of coverting all of my MySQL to MySQLi.
I have been doing much research on this but find it a bit confusing.
I have two questions at this point regarding the matter:
1) What does it exactly mean to "escape" a string and where does the code for this go? I assume it goes on my page with my database login credentials.
I found the following but find it somewhat hard to interpret:
"We'll use the mysqli_real_escape_string() function. Since it needs a database connection, we'll go ahead and wrap it in its own function. In addition, since we only need to escape strings, we might as well quote the value at the same time:"
`
function db_quote($value) {
$connection = db_connect();
return "'" . mysqli_real_escape_string($connection,$value) . "'";
}
`
"If we are not sure of the type of value we pass to the database, it's always best to treat it as a string, escape and quote it. Let's look at a common example - form submission. We'll use our previous INSERT query with user input:"
`
// Quote and escape form submitted values
$name = db_quote($_POST['username']);
$email = db_quote($_POST['email']);
// Insert the values into the database
$result = db_query("INSERT INTO `users` (`name`,`email`) VALUES (" . $name . "," . $email . ")");
`
2) After I have this set up in my code, how do I properly test that it is indeed working (Without completly wiping out my tables, etc?)
I just really need some further explanations on this subject before I begin the process.
Any resources, advice, or pointers in the right direction would be greatly appreciated.
Escaping a string means converting characters that would be treated as special in a query as literal characters. A good example is a single quote. This has special meaning in a query, and attackers can use it to alter the functionality of the query to bypass security. Escaping that character makes it non-functional as part of the query such that it only contributes to a string value. Details on that can be found by studying SQL injection attacks.
As for where it goes, anytime you build a query with uncontrolled values, those values should be escaped.
**After I have this set up in my code, how do I properly test that it is indeed working (Without completly wiping out my tables, etc?)**
My preference is a combination of extracting the final query for manual testing, and extensive use of a test database so that the integrity of your production database isn't affected during development.
I was reading lots of forums and answers on Stack over flow regarding SQL-Injection
and i came to know this is very basic level of SQL-injection
$_POST['name'] = 'xyz;DROP Table users';
mysqli_query ('select * from abc where name='."$_POST['name']")
To prevent this
Use mysqli_escape_stirng on any input that comes from user can save me from SQl-injection
Use PDO and prepare statement can also save me from SQL-injection
Q1. What i want to know here how passing data to Mysqli_escape_string can save me from SQL-Injection
$safe_variable = mysqli_escape_String($connection ,$_POST['name'];
How mysqli_escape_string will only save "XYZ" from POST data and leave the rest of the part (if that is the case)
Q2. How PDO will save me from SQL-Injection
$stmt = $dbh->prepare("select * from ABC where name = :name");
$stmt->bindParam(':name',$name);
$name = $_POST['name'];
$stmt->execute();
Any help in this regard his highly appreciated
The problem with incorporating user input into SQL is that in the resulting SQL you can’t tell which parts were provided by the developer and which by the user. That’s why the developer must ensure that user input gets interpreted as intended.
This is where string escaping functions and parameterization come in:
String escaping functions like mysqli_real_escape_string process the value so that it can be securely used in a string literal without fearing it may be interpreted as anything else than string data.
However, it is important to note that the value is actually placed in a string literal and nowhere else as it’s only intended for that specific purpose, i. e., it ensures that the passed data is interpreted as string data only when placed inside a string literal. Unfortunately, the PHP manual fails to mention the string literal part.
Parameterization as implemented by prepared statements separate the SQL and the data parameters. So there can’t be a confusion of SQL code and provided data. With server-side prepared statements first the statement gets prepared having only parameter placeholders and then the parameter values get passed for execution. And whenever a parameter is encountered, the DBMS uses the corresponding parameter value.
As for your specific example:
What i want to know here how passing data to Mysqli_escape_string can save me from SQL-Injection
$safe_variable = mysqli_escape_String($connection ,$_POST['name'];
How mysqli_escape_string will only save "XYZ" from POST data and leave the rest of the part (if that is the case)
It doesn’t because you didn’t put the value in a string literal. However, the following would work:
mysqli_query("select * from abc where name='$safe_variable'")
How PDO will save me from SQL-Injection
$stmt = $dbh->prepare("select * from ABC where name = :name");
$stmt->bindParam(':name',$name);
$name = $_POST['name'];
$stmt->execute();
As already said, you explicitly state what the SQL looks like by preparing the statement. And then you pass the parameters for execution. As the parameterized SQL and its parameters are separated, they won’t mix and a passed parameter value can’t be mistaken as SQL.
Q1:
mysql(i)_real_escape_string() calls MySQL's library function
mysql(i)_real_escape_string, which prepends backslashes to the following
characters: \x00, \n, \r, \, ', " and \x1a.
(http://php.net/mysqli_real_escape_string)
Note that this depends on the character encoding (not workin in this case is SET NAMES ... (security risk!!!), $mysqli->set_charset('utf8'); should be used!). (You can read about encoding in my post Mastering UTF-8 encoding in PHP and MySQL.)
How does it prevent SQL injection?
- Well it prevents breaking the variables context by escaping ' etc, the thing is, that mysql_query and mysqli_query only execute one query per query, that means, it simply ignores ;DROP Table users.
mysqli_real_escape_string DOES NOT prevent inserting code like DROP DATABASE.
Only PDO and/or mysqli_multi_query are vulnerable in this case.
Q2:
The statement is sent to the server first, then the bound variables will get sent seperated and then the statement gets executed, in this case, the security is provided by the database library, not by the client library. You should prefere this.
That means, you first send $dbh->prepare("select * from ABC where name = :name"); to the server and the database knows your bind param will be inserted into the :name placeholder and it will automatically wrap it properly to not break out of its supposed context. The database will try to look for a name value of xyz;DROP Table users and it won't executed any command, just fill that variable space.
I think this is the case for most SQL escaping functions:
They escape the control chars like ;, ', ", ...
So your string
xyz;DROP Table users
Will be escaped by the functions to
xyz\;DROP Table users
So your string now isn't a valid SQL command anymore.
But be aware of HTML tags in the data stored in a DB.
If I insert for example
<script>alert('foobar');</script>
This will be stored in DB and not treated by the SQL escape functions. If you print out the field somewhere again, the JS will be executed by the visitors browser.
So use in addtion htmlspecialchars() or htmlentities() for sanitize user input. This is also true for prepared statements.
I wrote a script to insert record in my DB. The only issue I am getting is when I try to store data which contains ' character then the script does not work and it does not store anything in the DB. For example John's Birthday , Amy's Home etc . Any solution to this problem which allows special character like ' to store in the DB and retrieving them without any harm to security?
mysqli_query($con,"INSERT INTO Story (desc)
VALUES ('$mytext')");
PHP's mysqli_real_escape_string is made specifically for this purpose. You problem is that quotes are being interpreted by MySQL as part of the query instead of values. You need to escape characters like this so they won't affect your query - this is what SQL injection is.
$mytext = mysqli_real_escape_string($con, $mytext);
// continue with your query
Manual: http://php.net/manual/en/mysqli.real-escape-string.php
Filter the variable part of the query through mysqli_real_escape_string.
I'm still learning about SQL injection, but always the best way for me was using examples, so this is part of my code:
$sql = "INSERT INTO `comments` (`id`, `idpost`, `comment`, `datetime`, `author`, `active`)
VALUES (NULL, '" . addslashes($_POST['idcomment']) . "', '" .
addslashes($_POST['comment']) . "', NOW(), '" .
addslashes($_POST['name']) . "', '1');";
mysql_query($sql);
Knowing that all the POST vars are entered by the user, can you show me how can i make an injection to this script? so i can understand more about this vulnerability. Thanks!
my database server is MySQL.
Don't use addslashes(), always use mysql_real_escape_string(). There are known edge cases where addslashes() is not enough.
If starting something new from scratch, best use a database wrapper that supports prepared statements like PDO or mysqli.
Most of the other answers seem to have missed the point of this question entirely.
That said, based on your example above (and despite your code not following the best practice use of mysql_real_escape_string()) it is beyond my ability to inject anything truly detrimental when you make use of addslashes().
However, if you were to omit it, a user could enter a string into the name field that looks something like:
some name'; DROP TABLE comments; --
The goal is to end the current statement, and then execute your own. -- is a comment and is used to make sure nothing that would normally come after the injected string is processed.
However (again), it is my understanding that MySQL by default automatically closes the DB connection at the end of a single statement's execution. So even if I did get so far as to try and drop a table, MySQL would cause that second statement to fail.
But this isn't the only type of SQL injection, I would suggest reading up some more on the topic. My research turned up this document from dev.mysql.com which is pretty good: http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf
Edit, another thought:
Depending on what happens to the data once it goes to the database, I may not want to inject any SQL at all. I may want to inject some HTML/JavaScript that gets run when you post the data back out to a webpage in a Cross-Site Scripting (XSS) attack. Which is also something to be aware of.
As said before, for strings, use mysql_real_escape_string() instead of addslashes() but for integers, use intval().
/* little code cleanup */
$idcomment = intval($_POST['idcomment']);
$comment = mysql_real_escape_string($_POST['comment']);
$name = mysql_real_escape_string($_POST['name']);
$sql = "INSERT INTO comments (idpost, comment, datetime, author, active)
VALUES ($idcomment, '$comment', NOW(), '$name', 1)";
mysql_query($sql);
Addslashes handles only quotes.
But there are some more important cases here:
Be careful on whether you use double or single quotes when creating the string to be escaped:
$test = 'This is one line\r\nand this is another\r\nand this line has\ta tab';
echo $test;
echo "\r\n\r\n";
echo addslashes($test);
$test = "This is one line\r\nand this is another\r\nand this line has\ta tab";
echo $test;
echo "\r\n\r\n";
echo addslashes($test);
Another one:
In particular, MySQL wants \n, \r and \x1a escaped which addslashes does NOT do. Therefore relying on addslashes is not a good idea at all and may make your code vulnerable to security risks.
And one more:
Be very careful when using addslashes and stripslashes in combination with regular expression that will be stored in a MySQL database. Especially when the regular expression contain escape characters!
To store a regular expression with escape characters in a MySQL database you use addslashes. For example:
$l_reg_exp = addslashes( �[\x00-\x1F]� );
After this the variable $l_reg_exp will contain: [\\x00-\\x1F].
When you store this regular expression in a MySQL database, the regular expression in the database becomes [\x00-\x1F].
When you retrieve the regular expression from the MySQL database and apply the PHP function stripslashes(), the single backslashes will be gone!
The regular expression will become [x00-x1F] and your regular expression might not work!
Remember, that the magic may happen in:
addslashes which may miss something
before adding to database
after retrieving from database
Your example is just an excerpt. The real problem might not be visible here yet.
(based on comments from php.net which are very often more valuable than the manual itself )
In accessing my database, I have the user fill out a form, and in the target page, the posted values are used in the resulting MySQL query.
$query = mysql_query("SELECT pass FROM database WHERE user='$_POST[user]'");
However, for some reason or another, MySQL doesn't like my using a $_POST variable in the command, and it only works if I define (for example) $user = $_POST['user'];, and then put $user directly in the SQL command.
On the other hand, I can use $_POST values in INSERT statements where specific column names are not required:
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', '$_POST[user]'");
If I try an INSERT statement where attributes are defined (e.g. user='foo'), then the same problem appears.
What am I doing wrong in my SQL query that causes the command to error out when run, but works with the specific method of formatting an INSERT command?
Hopefully, it's not "tough luck, looks like you have to assign all of your posted values". Heh.
First of, watch out for SQL Injections!
Now, to answer your question try doing this instead:
$query = mysql_query("SELECT `pass` FROM `database` WHERE `user` LIKE '" . mysql_escape_string($_POST['user']) . "';");
You were doing a couple of things wrong:
using the = operator instead of LIKE operator
not enclosing the value in the SQL query with '
not enclosing the user index in the $_POST array with '
PS: You should use mysql_real_escape_string() instead of mysql_escape_string()!
You're simply inserting a variable into a string, so it shouldn't matter which command you're putting it into.
There are a few issues to point out.
One, you might want to use the {} format for array variables. You don't use quotes around the arrray key names in this format.
$query = mysql_query("SELECT pass FROM database WHERE user='{$_POST[user]}'")
Two, you'd never want to make a query like that because you are open to sql injection holes. Consider, what if $_POST['user'] was "cow';drop table database;--"?
You must either run mysql_real_escape_string on the POST input before putting it into your query, or check out using PHP PDO with prepared statements.
One way to do format your string which provides a bit of structure is to use sprintf.
$query=mysql_query(sprintf("SELECT pass FROM database WHERE user='%s'",mysql_real_escape_string($_POST['user'])));
Use PDO - it provides much better API to communicate with DB.
If you're using mysql_*() functions always remember to filter (mysql_real_escape_string()) any data that comes from untrusted source (like user)
Pay more attention to how your code looks like. Just compare the following listings:
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ", " . mysql_real_escape_string($_POST['user']) . ")");
$query = sprinf('INSERT INTO database VALUES ("foo", "bar", "%s", "%s", "%s")',
mysql_real_escape(...), ...);
Do I have to explain which one is better to read, modify or understand?
Why not check and see what mysql_error() has to say about it? If your query is invalid, mysql_error() will return a nice blob of text telling you exactly what went wrong.
As for MySQL not liking the POST var if you insert it directly for some runs, but not others, then you should make sure you're using consistent data and setups for each test. If some test are done using a GET, then your POST vars will be empty. If you're using different user names for each test, then see if what's consistent between the ones that fail.
And as mentioned above, read up about SQL injection and how your query is just begging to be subverted by a malicious user.
Try
$query = mysql_query("SELECT pass FROM database WHERE user=" . mysql_real_escape_string($_POST['user']));
and
$query = mysql_query("INSERT INTO database VALUES ('foo', 'bar', " . mysql_real_escape_string($_POST['user']) . ")");
Its always a good idea to sanitize anything received through $_GET or $_POST