I've been looking through a bunch of topics but haven't found anything that works yet - or maybe it does, but I'm pretty new to PHP/MySQL, so maybe I'm just missing something.
I've got a page that allows users to type in the name of an item to look it up in the (MySQL) database. It then generates a table of results and displays those, and turns some of the data into links. The linked terms are then used as queries to search other fields of the database.
Everything works... except when one of the search terms contains an apostrophe. Obviously I want to prevent injection issues, but I also need to search with the apostrophes. Here's the code I have:
$query = mysql_real_escape_string($query);
$query = htmlspecialchars($query);
$queryentered = $query;
$query = strtolower($query);
And here's the URL that's it's passing to the query:
echo " from <a href='index.php?volume=".$results2['book']."' class='where'>"
.$results2['book']."</a>";
And the query:
$raw_results = mysql_query("SELECT * FROM $database1 WHERE $wh LIKE '%$query%'
ORDER BY displayname") or die(mysql_error());
What am I not encoding right? When I run a search, it displays "No results found for [search term]", where I'm getting \' for any apostrophes.
This snippet is in exactly the wrong order:
$query = mysql_real_escape_string($query);
$query = htmlspecialchars($query);
$queryentered = $query;
$query = strtolower($query);
Escaping should be done last, right before the database query string concatenation. In this case it's irrelevant as strtolower does not undo the escaping. More severe is actually the HTML escaping, which does not belong in SQL context. Don't overwrite your main variable, instead:
$queryentered = htmlspecialchars($query);
$query = strtolower($query);
$query = mysql_real_escape_string($query);
The cumbersome database escaping, btw, is easy to avoid. Look into PDO and prepared statements.
problem is in this line:
$query = htmlspecialchars($query);
If you search for Death's book your final $query would be death\'s book!
By removing htmlspecialchars your query would be fine!
I'd STRONGLY recommend using prepared statements and the PDO library.
Really easy to learn
Will make your question easier to solve
Works with (most) any database
Goes a long way to prevent the injection issues you mention
Skill set will be applicable to other languages you may learn
All you have to do is write the query, then prepare a connection string (with the database type and details) and then fire off the query. An array of parameters is sent with the query, to decouple the data.
Helpful links:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
http://id1.php.net/pdo
Related
I have the following simple search query code:
function explode_search($squery, $column, $db, $link) {
global $conn;
$ven = explode(' ', safeInput(str_replace(',', '', $squery)));
$ven2 = array_map('trim', $ven);
$qy = ''.$column.' LIKE "%'.implode('%" AND '.$column.' LIKE "%', $ven2).'%"';
$query = 'SELECT DISTINCT '.$column.', id, work_composer FROM '.$db.' WHERE '.$qy.' ORDER BY '.$column.' LIMIT 100';
$result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($result)) {
echo '<div><span class="mdmtxt" style="margin-bottom:5px;">'.$row[$column].'</span> <span class="mdmtxt" style="opacity:0.6;">('.fixcomp(cfid($row['work_composer'], 'cffor_composer', 'composer_name')).')</span></div>';
}
}
(The safeInput function removes ' and " and other possible problematics)
It works alright up to a point.
When someone looks for 'Stephane' I want them also to find 'Stéphane' (and vice versa) or if they are looking for 'Munich', 'Münich' should show up in the list as well.
Is there a way to make MySQL match those search queries, irrespective of the special characters involved?
You want to use what's called a "Parameterized Query". Most languages have them, and they are used to safeguard input and protect from attacks.
They're also extremely easy to use after you get used to them.
You start out with a simple query like this
$stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?")
and you replace all your actual data with ?s.
Then you bind each parameter.
$stmt->bind_param("s", $city);
Then call $stmt->execute();
More information can be found here: http://php.net/manual/en/mysqli.prepare.php
One time in college, our prof made us use a library that didn't have parameterized queries, so I wrote an implementation myself. Doctrine, which is pretty awesome, handles all this for you. I would always rely on someone else to do this stuff for me instead of writing my own implementation. You'll get into trouble that way. There's also a lot written about not reinventing new types which is basically what you're doing. Types have problems. Types need testing. This kind of thing is well tested in other implementations and not yours.
iam using this code below, but the character "&" will not be inserted into the db, also when i copy/paste some text from other pages and put it into the db the text ends for example in the middle of the text, dont know why, i tried also addslashes() and htmlspecialchars() or htmlentities().
i read mysqli_real_escape_string() is againt SQL injection attacks and htmlspecialchars() against XSS attachs, should i also combine them ?
$beschreibung = mysqli_real_escape_string($db, $_POST['beschreibung']);
SQL Injection is merely just improperly formatted queries. What you're doing is not enough, stop now. Get into the practice of using prepared statements..
$Connection = new mysqli("server","user","password","db");
$Query = $Connection->prepare("SELECT Email FROM test_tbl WHERE username=?");
$Query->bind_param('s',$_POST['ObjectContainingUsernameFromPost']);
$Query->execute();
$Query->bind_result($Email);
$Query->fetch();
$Query->close();
Above is a very basic example of using prepared statements. It will quickly and easily format your query.
My best guess to what is happening, I'm assuming you're just using the standard:
$Query = mysqli_query("SELECT * FROM test_tbl WHERE Username=".$_POST['User']);
As this query is not properly formatted you may have the quotes in your chunk of text which close the query string. PHP will then interpret everything as a command to send to the SQL server
If you know what you are doing, you can escape indata yourself and add the escaped data to the query as long as you surround the data with single quotes in the sql. An example:
$db = mysqli_connect("localhost","my_user","my_password","my_db");
$beschreibung = mysqli_real_escape_string($db, $_POST['beschreibung']);
$results = mysqli_query(
$db,
sprintf("INSERT INTO foo (beschreibung) VALUES ('%s')", $beschreibung)
);
To get predictable results, I advise you to use the very same character encoding, e,g, UTF-8, consistently through your application.
I've been reading about MySQL Injection as I've just started adding prepared statements to my site. I wanted to know a bit more about how it actually protects and after reading some, I don't think I've done it correctly.
I was reading from here: http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php
I really don't understand how it can be 'injected' or how prepared statements get around it. Particularly this bit:
Normal: SELECT * FROM customers WHERE username = 'timmy'
Injection: SELECT * FROM customers WHERE username = '' OR 1''
When using SELECT, the things I use for WHERE are only ID or username. They can't change their ID and the username is validated when they sign up by this:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
So is this "protect()" preventing MySQL injection?
If so, what are the uses for prepared statements? Is this protection correct?
Instead of
$car = 'nissan';
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
I put:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
If that is correct, I don't understand how that does anything besides just adding another line of code?
EDIT
First prepared statement, any good?
$getusername = mysqli_prepare($con, "SELECT users WHERE users.username = '",mysqli_real_escape_string($username),"'");
Firstly, let's look at your attempt at escaping:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
There are 2 mistakes here: Firstly, addslashes is doing the same job as mysqli_real_escape_string (but doing it badly), so remove that.
Secondly, you should always sanitise for the context you're working in. So, when generating SQL, you sanitise for SQL; when generating HTML, sanitise for HTML. So putting strip_tags in here doesn't make sense, because it's to do with HTML, but this is apparently an SQL escaping function. Do that separately, when you're preparing output in your templates.
In short, just mysqli_real_escape_string on its own would be better than what you have here.
Next, let's look at parameterising the query:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
This statement isn't prepared or parameterised - as far as the database is concerned, it's still just a string of SQL. If $car is actually set from user input (e.g. $car = $_GET['car'];), then you can insert any piece of SQL in the middle of the query:
$car = "nissan'; DROP TABLE stats; --";
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
// MySQL will now drop your table; oops!
// UPDATE stats SET stats.car = 'nissan'; DROP TABLE stats; --' WHERE stats.id = 4
Adding $car = mysqli_real_escape_string($car); will correctly escape the ' in the input, stopping it ending the string and starting a new SQL statement.
Parameterised queries avoid the problem a different way: they tell the database which parts of the query are supposed to be SQL, and which are just data provided by the user. It looks like this:
$car = "nissan'; DROP TABLE stats; --";
$query_with_placeholder = "UPDATE stats SET stats.car = ? WHERE stats.id = 4";
// Note the ?, without quotes, represents somewhere for data to be put by MySQL
$prepared_statement = mysqli_prepare($con, $query_with_placeholder);
// Now we tell MySQL what to put in the placeholder
mysqli_stmt_bind_param($prepared_statement, 's', $car);
// And execute it
mysqli_stmt_execute($prepared_statement);
Because $car is never actually inserted into the SQL, just passed as a separate parameter, there is no way of it injecting anything nasty, and we don't need to do any additional escaping.
Also note that mysqli_real_escape_string is to prevent injection with strings. If you have a (for example) integer field then escaping the passed variable to cope with a quote doesn't really help as injection doesn't require the quote to work.
For example:-
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".mysqli_real_escape_string($con, $some_field_expected_to_be_int);
would still give a piece of SQL reading:-
SELECT * FROM users WHERE id = 1 OR 1=1
and return everything.
In such a case you should make sure it is an integer:-
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".(int)$some_field_expected_to_be_int;
Personally both prepared statements and escaping fields have their places. Prepared statements make it harder to forget to escape items and with many databases they give a performance advantage, but the mysqli placeholders are not readable, getting a statement out for debugging is not workable and it becomes a nightmare when used for heavily dynamic sql. But escaping leaves you with the risk you will forget somewhere and the code can look messy.
EDIT - to add a bit on prepared statements.
We use a couple of in house database classes which separate things a bit from mysql / mysqli / pdo / sql server, but for a basic example of using prepare and a bound parameter:-
<?php
if($stmt = $this->db->prepare("SELECT users WHERE users.username = ? "))
{
$stmt->bind_param('s', $username);
$stmt->execute();
}
?>
In this the SQL is not built up using the variable (which could have been manipulated), rather the variable is passed as a parameter.
Escaping the data and using prepared queries both protect against SQL injection. But escaping is more error-prone, because it's easy to forget to call it in all the places you need it. If you use prepared statements, the protection happens automatically.
There's no significant difference between
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
and
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
This is just a stylistic choice. The second version can be useful when you want to insert debugging statements, e.g.
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
echo $query;
$update_car = mysqli_query($con,$query);
Popular practice is to both sanitize input and use prepared statements with your queries whenever you have a possible user input variable.
Sanitation should also prevent users from overflowing your variables and upload large quantities of data to your application.
Preparing statements is your second measurement of security, it prevents mistakes you might make later, like forgetting to escape input or not realising what is actually user input next time you refactor your application.
Any time you don't properly guard against injection, you open your project up to users deleting data, modifying data or uploading malicious code to your application. (Do you really want someone to upload javascript in a textfield?)
I've made a simple search-script in PHP that searches a mySQL database and outputs the result. How this works is like this:
User searches for "jack's" through a search-form.
My PHP-script GETs this search, and sanitizes it.
Then the script, with the use of SELECT and LIKE, gets the results.
The script then outputs the result to the user.
Lastly, the script tells the user that "jack's returned x results." with the help of escaping.
What I would like to ask is, am I doing it right?
This is how I sanitize before SELECTING from the database:
if(isset($_GET['q'])){
if(strlen(trim($_GET['q'])) >= 2){
$q = trim(mysql_real_escape_string(addcslashes($_GET['q'], '%_')));
$sql = "SELECT name, age, address FROM book WHERE name LIKE '%".$q."%'";
}
}
And this is how I escape before outputting "jack's returned x results.":
echo htmlspecialchars(stripslashes($q)) . " returned x results.";
Is this the correct way to do it?
By the way, I know that PDO and mySQLi is preferred as they sanitize themselves through the use of prepared statements, but I have no real experience with them whatsoever. But I would gladly take a look, if you guys could link me some newbie tutorials/explanations.
Furthermore, I heard that magic_quotes and charset could in some way or another lead to injections -- is this correct?
For some reason we need also escape a backslash too.
So, the proper code would be, I believe
if(isset($_GET['q'])){
$_GET['q'] = trim($_GET['q']);
if(strlen($_GET['q']) >= 2){
$q = $_GET['q'];
$q = '%'.addCslashes($q, '\%_').'%';
// now we have the value ready either for escaping or binding
$q = mysql_real_escape_string($q);
$sql = "SELECT name, age, address FROM book WHERE name LIKE '$q'";
//or
$sql = "SELECT name, age, address FROM book WHERE name LIKE ?";
$stm = $pdo->prepare($sql);
$stm->execute(array($q));
$data = $stm->fetchAll();
}
}
For the output, use
echo htmlspecialchars($_GET['q']);
stripslashes not needed here.
Furthermore, I heard that magic_quotes and charset could in some way or another lead to injections -- is this correct?
magic quotes won't harm your security if you won't use them.
charset is dangerous in case of some extremely rare encodings but only if improperly set. if mysql(i)_set_charset or DSN (in case of PDO) were used for the purpose - you are safe again.
As for PDO, a tag wiki should be enough for starter, I believe
I have been doing a bit of research on SQL injections and so far all I can see is when you are concatenating query strings with variables, you have problems.
My question(s) is/are:
If I have this code:
$query = "SELECT id, name, inserted, size FROM products";
$result = odbc_exec($conn, $query);
Am I subject to sql injection? I didn't think so but I found a post on stackoverflow that indicated that it was.
Now if I have this code:
$variable = "name";
$query = "SELECT"' .$variable. ' FROM products";
$reulst = odbc_exec($conn, $query);
Am I still stubject to injection? It seems to me that I have full control of that variable and the code is run on the server side so that would be safe. Is this correct?
Thanks in advance for any input!
SQL injection is usually a problem if you have input from a source you can't trust. Seeing as this is the case in neither of your examples, you're fine as far as malicious attacks go.
However, it is good practice to escape $variable before inserting it into the query string even if you control it.
You are prone to sql injection if you allow any user input into your queries at all. With the two examples provided, nothing is being input from the user, so these are both safe.
The first query is not subject to injection as there are no dynamic parts to it.
The second is. Even if you think you have full control over your variable, if you are using user supplied data (whether coming from form sumbit, cookies etc...), you can be vulnerable.
Always use parameterized queries through a SQL library that will ensure data is safely escaped.
The only case when a query can get exposed is when parts of it are passed from input.
$variable = $_GET["name"];
$query = "SELECT " .$variable. " FROM products"; // now things can get bad
$reulst = odbc_exec($conn, $query);
One correct way of using input variables inside a query would be to escape them:
$variable = addslashes($_GET["name"]); // sanitizing input
$query = "SELECT " .$variable. " FROM products"; // all good here
$reulst = odbc_exec($conn, $query);
The issue is "where do the values of your variables come from?" If you are using user-submitted data in a query, then you need to be careful how you use it. In your case, you're safe as far as I can tell.
For the first example, no, you won't be subject to any SQL injection because there is nothing a user could input to change your query.
In the second case, you're only subject to injection if $variable is derived from some user input.
Any time you take user input straight into your $query you are susceptible to sql injection. The first code you show is fine, its a straight query, there is no way for the user to input any harmful code.
$query = "SELECT id, name, inserted, size FROM products";
$result = odbc_exec($conn, $query);
But the second code snippet (if $variable is taken from a form ) anyone could enter in harmful code and kill your database.
$variable = $_POST['name']; **for instance, this would be bad!
$query = "SELECT"' .$variable. ' FROM products";
$reulst = odbc_exec($conn, $query);
How does $variable get its value?