Correct usage of $mysqli->real_escape_string in PHP - php

When I call $mysqli->real_escape_string($str), I get back a safe string that I can use in a query. It has all special symbols that can lead to SQL Injection Attack escaped.
My question is - what is the correct way to use it in a query later? Do I single quote or double quote this safe string?
For example,
$safe_title = $mysqli->real_escape_string($_POST['safe_title']);
Now do I do this:
$r = $mysqli->query("SELECT * FROM articles WHERE title='$safe_title'");
Or this:
$r = $mysqli->query('SELECT * FROM articles WHERE title="'.$safe_title.'"');
Sometimes I do one, sometimes I do the other, but this one time when I did one of these and typed a bunch of garbage in input, I got an SQL query error somehow. I realized I'm doing it wrong and all my code is probably vulnerable. That's why I'm asking this question here.

You would do this:
$r = $mysqli->query("SELECT * FROM articles WHERE title='$safe_title'");
But as #JordiKroon pointed out prepared statements are preferred.
$stmt = $mysqli->prepare("SELECT * FROM articles WHERE title=?");
$stmt->bind_param("s", $safe_title);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_array(MYSQLI_NUM))
{
foreach ($row as $r)
{
print "$r ";
}
print "\n";
}

Related

How to deal with apostrophes and double quotes simultaneously in PHP

I have a HTML form, from which a PHP script extracts values, as shown below:
$dbc = mysqli_connect("all required info here...") or die("Error occurred");
$sent = "Any sentence here...which may contain apostrophe or double quotes or both";
$query = "SELECT * FROM myrecord WHERE sentence = '$sent'";
$result = mysqli_query($dbc, $query);
$data = mysqli_fetch_array($result);
mysqli_close($dbc);
The problem is, that the variable $sent can contain any string with a combination of either apostrophe or double quotes or both. This gives an error when going for execution of mysqli_query().
So even if I escape double quotes in initialization of $sent it will still create problem for execution of mysqli_query(). And if I escape both for ' and " then value of $sent does not remains what it actually needs to be (although I am not sure about whether escaping both ' and " will work or not).
Is there any built in function that automatically escapes all special characters of a string? Or any workaround that solves this problem?
[P.S. I have already searched some previous questions on stackoverflow and haven't been able to find a solution.]
What you want, and what you should do is used prepared statements (parameterized queries). With PDO, that would look something like this:
$stmt = $pdo->prepare('SELECT * FROM myrecord WHERE sentence = :sentence');
$stmt->execute([':sentence' => $sentence]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
//do stuff
}
mysqli supports them, too, but the API is slightly more cumbersome (IMO) - see mysqli_prepare docs for details:
$stmt = $mysqli->prepare('SELECT * FROM myrecord WHERE sentence = ?');
//or $stmt = mysqli_prepare($connection, $query);
Then, you bind the parameter (the value to be used in the WHERE clause) using bind_param:
$stmt->bind_param('s', $sentence);
//or mysqli_stmt_bind_param($stmt, 's', $sentence);
Then call execute - or mysqli_stmt_execute, and fetch the results using fetch - or mysqli_stmt_fetch...
As mentioned in the comments: the parameters and query string needn't be quoted in any way, because they're treated as separate entities. The result being that you can re-use the same prepared statement with different paramters:
$stmt = $pdo->prepare('SELECT * FROM table WHERE field = :field');
$fieldVals = [123, 46, 32]; // three values:
$results = array_fill_keys($fieldVals, null);
foreach ($fieldVals as $val) {
$stmt->execute([':field' => $val]);//execute with each value in $fieldVals array
$results[$val] = $stmt->fetchAll(PDO::FETCH_ASSOC); // fetch results for this field value
//optional, but good form:
$stmt->closeCursor();
}
you've now used the same statement 3 times, but only had to send the query string once. The query had to be parsed and processed once, and after that, you merely sent the paramters to the DB. This approach is generally faster, safer (prepared statements protect agains most injection attacks), and just all round better.

Update to prepared SQL statement not returning results

I'm having trouble updating a site. With security in mind, I am trying to rewrite the SQL statements using PDO prepared. It's my preferred choice generally.
The site I'm working on has this query, returning results via json to a search box. It works ...
$sql = "SELECT * FROM stock_c_ranges WHERE deleted = 'no' AND current_status = 'current'";
$result = mysqli_query($conn, $sql);
$results_list = array();
while($row = mysqli_fetch_array($result)){
$results_list[] = $row['manufacturer_name'].' ID:'.$row['id'];
}
echo json_encode($results_list);
I've re-written using prepared statements ...
$range = "SELECT * FROM stock_c_ranges WHERE deleted= :deleted AND current_status= :cstatus";
$deleted='no';
$c_status='current';
$result_lists=array();
$stmt=$pd_con->prepare($range);
$stmt->bindParam(':deleted',$deleted,PDO::PARAM_STR);
$stmt->bindParam(':cstatus',$c_status,PDO::PARAM_STR);
$stmt->execute;
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
$results_list[] = $row['manufacturer_name']. 'ID:'.$row['id'];
}
echo json_encode($results_list);
..... this doesn't
I've either made a glaring syntax error that I'm just blind to after looking at it for so long, or there is something about using PDO and JSON/AJAX that I'm not aware of stopping it functioning.
Apologies, writing it on here has highlighted the glaringly obvious ...
$stmt->execute;
Should have been ...
$stmt->execute();

Accessing a MySQL Link Identifier from within a Function

I'm having some difficulty returning an array out of a while lopp which I have in a function. Here is the code I am using. I am meant to be able to return an array of results from the function which contains the id numbers of pictures associated with a particular user id - in this case I want to print_r the array for the user id of 17. When this code isn't in the function it works, but when I place it in the function, no luck. I presume its related to a mistake I am making in the returning of the array. Your help is greatly appreciated.
function picture($id)
{
$sql = "SELECT * FROM avatar WHERE user_id={$id}";
$result = $database->query($sql);
$results = array();
while ($row = mysql_fetch_assoc($result))
{
$results[] = $row;
}
return $results;
}
$results = picture(17);
print_r($results);
Your function can't access your MySQL link identifier
First of all, you're mixing object-oriented paradigm ($database->query($sql)) with procedural paradigm (mysql_fetch_assoc($result)) which will make your code a nightmare to maintain.
Assuming that $database is a mysql_ link identifier, you'll need to pass it into your function in order to access it there.
function getUserAvatar($database, $id){
$sql = 'SELECT * FROM `avatar` WHERE `user_id`=' . intval($id) . ' LIMIT 1;';
$result = mysql_query($database, $sql);
$row = mysql_fetch_assoc($result);
return $row;
}
$results = picture($database, 17);
Don't just copy-paste that, keep reading!
The above will probably work, but if you're allowing a user to pass that user ID into the function, it's quite possible that they'll be able to find a vulnerability to inject an SQL statement of their choice into your MySQL database.
mysql_ functions are deprecated, so you should ideally stop using them and switch to mysqli or PDO. You'll also want to get an understanding of prepared statements in order to prevent SQL injections. If you can't upgrade, look at the mysql_real_escape_string and intval functions and make sure you sanitize all user inputs before processing them.
The resulting code will look something like this, if you switch to mysqli and prepared statements:
function getUserAvatar($db, $userId) {
$stmt = $db->prepare("SELECT * FROM `avatar` WHERE `user_id`=? LIMIT 1;");
$stmt->bind_param("i", $userId);
$stmt->execute();
$res = $stmt->get_result();
return $res->fetch_assoc();
}
$db = new mysqli("localhost", "user", "password", "database");
$result = getUserAvatar($db, 17);
may be you should try this..
function picture($id)
{
$sql = "SELECT * FROM avatar WHERE user_id={$id}";
$result = $database->query($sql);
$row = mysql_fetch_assoc($result);
return $row;
}
$results = picture(17);
print_r($results);

Is this vulnerable to SQL injection

I realize there are a lot of questions already about this. But my method isn't the same as theirs, so I wanted to know. I think I understand SQL, but I don't want to risk making a mistake in the future, so thanks for any help. (This is just a project I'm doing, not homework or anything important).
function checkLogin($username, $password) {
$username = strtolower($username);
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");
$dbpassword = "";
while($row = mysql_fetch_array($result))
{
$rowuser = $row['username'];
if($username != $row['username']) continue;
$dbpassword = $row['password'];
}
if($dbpassword == "") {
return false;
}
$genpass = generatePassword($password);
return $genpass == $dbpassword;
}
So hit me with your best shot :)
And I don't think my method is as efficient as it could be. I don't understand php enough to understand what $row = mysql_fetch_array($result) is doing unfortunately.
Because you are taking an arbitrary string and placing it directly into an SQL statement, you are vulnerable to SQL injection.
( EDITED based on a comment below. )
The classic example of SQL injection is making a username such as the following:
Robert'); DROP TABLE users;--
Obligatory XKCD link
Explanation:
Given the "username" above, interpolation into your string results in:
SELECT * FROM `users` WHERE username='Robert'); DROP TABLE users;--'
The comment symbol -- at the end is required to "get rid" of your closing quote, because I just substituted one of mine to end your select statement so that I could inject a DROP TABLE statement.
As #sarnold pointed out, PHP's mysql_query only executes a the first query in the string, so the above example (known as query stacking) does not apply. The function is explained here: http://php.net/manual/en/function.mysql-query.php.
A better example can be found here. Here they use a username of
' OR 1 OR username = '
which interpolated becomes
SELECT * FROM `users` WHERE username='' OR 1 OR username = ''
and which would cause your application to retrieve all users.
The short answer is yes.
A perhaps more helpful answer is that you should never trust user input; prepared statements are the easiest way to protect against this, if you have PDO available. See PDO Prepared Statements
<?php
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE username=?");
if ($stmt->execute($username)) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
?>
The other answers are an excellent description of your problem, however, I think they both overlook the best solution: use PHP's PDO Prepared Statements for your queries.
$stmt = $dbh->prepare("SELECT * FROM users where username = ?");
if ($stmt->execute(array($username))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
This is a small, simple example. There are more sophisticated ways of using PDO that might fit your application better.
When you use PDO prepared statements you never need to manually escape anything and so long as you use this slightly different style, you will never write an SQL injection vulnerability and you don't have to maintain two variables per underlying "data" -- one sanitized, one as the user supplied it -- because only one is ever required.
I would say yes it is open to SQL injection.
This is because you are taking user input in the form of $username and putting it into your SQL statement without making sure it is clean.
This is a function that I like to use in my applications for the purpose of cleaning strings:
function escape($data) {
$magicQuotes = get_magic_quotes_gpc();
if(function_exists('mysql_real_escape_string')) {
if($magicQuotes) {
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
}
else {
if(!$magicQuotes) {
$data = addslashes($data);
}
}
return $data;
}
Then you can use it like this:
$username = escape(strtolower($username));
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");

mysql_num_rows error in PHP with mysql_query

Hi i am too new too php and mysql and i want to count the member number due to the search made by user. However, mysql_num_rows doesnt work.
mysql_num_rows(mysql_query("SELECT * FROM members WHERE $title LIKE '%$_POST[search]%' LIMIT $start,$member_number"));
It says "mysql_num_rows(): supplied argument is not a valid MySQL result resource in ..."
NOTE: $title is a select menu which user choose where to search. LIMIT is, as you know :), number of member which is shown in a page.
And also $start= ($page-1)*$member_number; in order to set the first entry in that page. I think the problem is here but i cant solve it. :(
Your query probably has an error, in which case mysql_query will return false.
For this reason, you should not group commands like this. Do it like this:
$result = mysql_query("...");
if (!$result)
{ echo mysql_error(); die(); } // or some other error handling method
// like, a generic error message on a public site
$count = mysql_num_rows($result);
Also, you have a number of SQL injection vulnerabilities in your code. You need to sanitize the incoming $search variable:
$search = mysql_real_escape_string($_POST["search"]);
... mysql_query(".... WHERE $title LIKE '%$search%'");
if $start and $end come from outside, you also need to sanitize those before using them in your LIMIT clause. You can't use mysql_real_escape_string() here, because they are numeric values. Use intval() to make sure they contain only numbers.
Using a dynamic column name is also difficult from a sanitation point of view: You won't be able to apply mysql_real_escape_string() here, either. You should ideally compare against a list of allowed column names to prevent injection.
you have to use GET method in your form, not POST.
mysql_num_rows doesn't make sense here.
If you're using limit, you already know the number*.
If you want to know number, you shouldn't use limit nor request rows but select number itself.
// get your $title safe
$fields = array("name","lastname");
$key = array_search($_GET['title'],$fields));
$title = $fields[$key];
//escape your $search
$search = mysql_real_escape_string($_GET['search']);
$sql = "SELECT count(*) FROM members WHERE $title LIKE '%$search%'";
$res = mysql_query($query) or trigger_error(mysql_error()." in ".$sql);
$row = mysql_fetch_row($res);
$members_found = $row[0]
in case you need just 5 records to show on the page, no need for mysql_num_rows() again:
// Get LIMIT params
$member_number = 5;
$start = 0;
if (isset($_GET['page'])){
$start = abs($_GET['page']-1)*$member_number;
}
// get your $title safe
$fields = array("name","lastname");
$key = array_search($_GET['title'],$fields));
$title = $fields[$key];
//escape your $search
$search = mysql_real_escape_string($_GET['search']);
$sql = "SELECT count(*) FROM members
WHERE `$title` LIKE '%$search%'
LIMIT $start, $member_number";
$res = mysql_query($query) or trigger_error(mysql_error()." in ".$sql);
while($row = mysql_fetch_assoc($res){
$data[] = $row;
}
Now you have selected rows in $data for the further use.
This kind of error generally indicates there is an error in your SQL query -- so it has not been successful, and mysql_query() doesn't return a valid resource ; which, so, cannot be used as a parameter to mysql_num_rows().
You should echo your SQL query, in order to check if it's build OK.
And/or, if mysql_query() returns false, you could use mysql_error() to get the error message : it'll help you debug your query ;-)
Typically, your code would look a bit like this :
$query = "select ..."; // note : don't forget about escaping your data
$result = mysql_query($query);
if (!$result) {
trigger_error(mysql_error()." in ".$query);
} else {
// use the resultset
}

Categories