This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 7 years ago.
I don't know how to make this code safe. I tried to use mysql_real_escape_string in the query variable like
$query = "select * from products where id= " . mysql_real_escape_string($products);
but didn't work, also tried to escape in the products variable, but got the same result.
Any sugestions?
Thanks.
<?php
/ Define vars.
$conn = mysql_connect('localhost', 'test', 'test');
$products = isset($_GET["products"]) ? $_GET["products"] : "";
$query = "select * from products where id=$products";
// List elements.
if ($conn)
{
mysql_select_db('testsqli');
$result = mysql_query($query);
// Table head.
echo '<table cellspacing="5" cellpadding="5">';
echo '<tr>';
echo '<td>Description</td>';
echo '<td>Price</td>';
echo '</tr>';
// Empty table?
if (#mysql_num_rows($result)==0)
{
echo '<tr>';
echo '<td><i>That\'s all!</i></td>';
echo '</tr>';
}
// Listing data in table.
while ($row = #mysql_fetch_array($result))
{
echo '<td>'.$row['Description'].'</td>';;
echo '<td>'.$row['Price'].'</td>';;
}
echo '</table>';
}
?>
You still need to add quotes, (and use msqli instead) like:
$query = "select * from products where id='" . mysqli_real_escape_string($products)."'";
// or
$query = sprintf(
"select * from products where id='%s'",
mysqli_real_escape_string($products)
);
I'd use prepared statements instead of MySQL escaping. Escaping skips over some of the wildcards, such as '%' and '*' which could also provide unanticipated results.
$stmt = $dbh->prepare("SELECT * FROM PRODUCTS WHERE ID=?");
$stmt->bindParam(1, $products, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
// call the stored procedure
$stmt->execute();
Also, keep in mind the following measures keep it safe:
Never connect to the database as a superuser or as the database owner. Use always customized users with very limited privileges.
Check if the given input has the expected data type. (In this case, verify that $products is formatted as expected, i.e. if your product catalog has indices of only nine characters, make sure it's not 100 characters long.) PHP has a wide range of input validating functions, from the simplest ones found in Variable Functions and in Character Type Functions (e.g. is_numeric(), ctype_digit() respectively) and onwards to the Perl compatible Regular Expressions support.
If the application waits for numerical input, consider verifying data with ctype_digit(), or silently change its type using settype(), or use its numeric representation by sprintf().
Reference: http://php.net/manual/en/security.database.sql-injection.php
Related
This question already has answers here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
How can I prevent SQL injection in PHP?
(27 answers)
Reference - What does this error mean in PHP?
(38 answers)
Closed 2 years ago.
I have been using the same code for years and all of a sudden I'm having problems that I cannot figure out. I am making a very simple query to MySQL in PHP using a variable in the statement. When I use the variable, it returns no results. When I manually type in the value of the variable instead, it works. I use this syntax all day long and never have had a problem. What on earth is wrong?
$name = "Fred";
$query = "SELECT * FROM database WHERE name='".$name."'";
$result = mysqli_query($connection, $query);
if (mysqli_num_rows($result) != 0) {
echo "Found record.";
}
If I replace the $name variable with Fred, it finds the record. If I echo the query with the variable before it executes and place that exact statement into MySQL directly in phpMyAdmin, I also get the result. If I leave the statement as-is with the variable in place, I get no result. Please help.
your query states SELECT * FROM database WHERE name='".$name."', this means that your table name is database, now i dont know how you actually created this table but database is a MYSQL reserved keyword change the name of your table to something else or just change your query to
$query = "SELECT * FROM `database` WHERE name='$name'";
assuming that your database connection is fine your code should now work
also worth noting, whenever acquiring data from a database use prepared statements instead of raw data as it makes you vulnerable to sql injection, in your case your code should be something like this
$name = "Fred";
$stmt = $dbconnection->prepare("SELECT * FROM table_name WHERE name=?")
$stmt->bind_param("s", $name);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows != 0)
{
echo "Found record.";
}
this is more secure
You shouldn't use mysqli excepted for old projects you can't upgrade, it's outdated and suffers from potential sql injection vulnerabilities.
Instead, I recommand you to learn PDO and prepared statements.
Your request should look like this :
$name = 'Fred';
$sql = "SELECT * FROM my_user_table WHERE name = :name";
// You should have set your pdo instance in a script handling your database connexion and reusing it in any script making requests.
$result = $pdo->prepare($sql);
// Here you dynamically inject values in your request and tells pdo what type of data you are expecting
$result->bindValue(':name', $name, PDO::PARAM_STR);
$result->execute();
if( $result->rowCount()) {
echo "{$result->rowCount()} result(s) found";
}
else {
echo 'No result found';
}
Here's the official doc :
https://www.php.net/manual/fr/book.pdo.php
This will also more than probably fix your problem.
This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Closed 11 months ago.
I'm trying to create a select query with dynamic where clause and dynamic parameters but I always get error :
Warning: mysqli_stmt::bind_param(): Number of elements in type
definition string doesn't match number of bind variables
Which I sincerely do not understand since it seems the count is alright. So this is what the code really looks like in its rude format. I can't see what I'm doing wrong.
//get variables
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
//convert string to array
$socialArray = explode(',', $mediaArray)
//declare some variables to be used later
$andwhere = '';
$bp = '';
$socialmarray = ''
//get every value from array of social media
foreach($socialArray as $socialmedia){
$socialmarray .=$socialmedia.',';
$andwhere .= " AND socialmedianame=?";
$bp .='s';
}
//test strings
echo $wheres = $andwhere;//AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?
echo $bip = $bp.'s';//ssss
echo $validarayy = rtrim($socialmarray,',');//Facebook,Twitter,Twitch
//select query
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$selectquery->execute();
$resultquery = $selectquery->get_result();
Because:
You are using user-supplied data, you must assume that your query is vulnerable to a malicious injection attack and
the amount of data that is to be built into the query is variable/indefinite and
you are only writing conditional checks on a single table column
You should use a prepared statement and merge all of the WHERE clause logic into a single IN statement.
Building this dynamic prepared statement is more convoluted (in terms of syntax) than using pdo, but it doesn't mean that you need to abandon mysqli simply because of this task.
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
$media = array_unique(explode(',', $mediaArray . $otherMedia));
$count = count($media);
$conn = new mysqli("localhost", "root", "", "myDB");
$sql = "SELECT * FROM mediaservices";
if ($count) {
$stmt = $conn->prepare("$sql WHERE socialmedianame IN (" . implode(',', array_fill(0, $count, '?')) . ")");
$stmt->bind_param(str_repeat('s', $count), ...$media);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($sql);
}
foreach ($result as $row) {
// access values like $row['socialmedianame']
}
For anyone looking for similar dynamic querying techniques:
SELECT with dynamic number of LIKE conditions
INSERT dynamic number of rows with one execute() call
In your query:
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
The ? represents one parameter to pass in, and the evaluation of $wheres adds another three, giving you four total parameters.
bind_param() should take a string representing the types of the variables to insert as the first parameter, and the variables themselves as the subsequent parameters.
In your bind:
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$bip evaluates to ssss and $otherMedia is a single string ("House"). You might expect $validarayy to be three strings, but rtrim() returns a string. Thus, it is only one string ("Facebook,Twitter,Twitch"). You pass through two variables when the query is expecting four:
$conn->prepare("select * from mediaservices where socialmedianame=House AND socialmedianame=Facebook,Twitter,Twitch AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?"
To correct this, you'll want to convert $validarayy back to an array, and use the index for the various inputs:
$socialmarray2 = explode(',', $validarayy);
$selectquery->bind_param("$bip", $otherMedia, $socialmarray2[0], $socialmarray2[1], $socialmarray2[2]);
Also note that your sample code has a few missing semicolons; you'll need to fix these in order for your code to work correctly.
This can be seen working here.
Finally, note that even if you were to split the three strings out correctly, the selection of ... AND socialmedianame=Facebook AND socialmedianame=Twitter AND socialmedianame=Twitch will never match any results; socialmedianame can only contain one value. You're probably looking to substitute your AND statements with OR statements.
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.
What would be the best way to protect this query from sql injection?
This example is just an example, I've read a few articles on internet but can't get my head around parametrised queries. Any links to useful articles will get a vote up but I think seeing this example would help me best.
$id = $_GET["id"];
$connection = odbc_connect("Driver={SQL Server};Server=SERVERNAME;Database=DATABASE-NAME;", "USERNAME", "PASSWORD");
$query = "SELECT id firstname secondname from user where id = $id";
$result = odbc_exec($connection, $query);
while ($data[] = odbc_fetch_array($result));
odbc_close($connection);
Thanks,
EDIT: I didn't make it obvious but I'm using SQL Server not mysql.
This is just an example, it won't always be a number I'm searching on.
It would be nice if the answer used parametrised queries as many people suggest this and it would be the same for all query's instead of different types of validation for different types of user input.
I think PDO objects are the best.
In a nutshell, here is how you use them.
$databaseConnection = new PDO('mysql:host='. $host .';dbname=' . $databaseName, $username, $password);
$sqlCommand = 'SELECT foo FROM bar WHERE baz=:baz_value;';
$parameters = array(
':baz_value' => 'some value'
);
$preparedStatement = $databaseConnection->prepare($sqlCommand);
$preparedStatement->execute($parameters);
while($row = $preparedStatement->fetch(PDO::FETCH_ASSOC))
{
echo $row['foo'] . '<br />';
}
The values you would enter for the SELECT criteria are replaced with parameters (like :field_value) that begin with a colon. The paramters are then assigned values in an array which are passed separately.
This is a much better way of handling SQL queries in my opinion.
The parameters are sent to the database separately from the query and protects from SQL injection.
Use prepared statements. First build a statement with the odbc_prepare() function, then pass the parameters to it and execute it using odbc_execute().
This is much more secure and easier than escaping the string yourself.
Lewis Bassett's advice about PDO is good, but it is possible to use prepared statements with ODBC without having to switch to PDO.
Example code, untested!
try {
$dbh = new PDO(CONNECTION_DETAILS_GO_HERE);
$query = 'SELECT id firstname secondname from user where id = :id';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$result = $stmt->execute();
$data = $stmt->fetchAll();
} catch (PDOException $e)
echo 'Problem: ', $e->getMessage;
}
Note: $e->getMessage(); may expose things you don't want exposed so you'll probably want to do something different on that line when your code goes live. It's useful for debugging though.
Edit: Not sure if you wanted a PDO or ODBC example but it's basically the same for both.
Edit: If you're downvoting me please leave a comment and tell me why.
To begin with, be careful with the variables you use in your queries, specially those that come from external sources such as $_GET, $_POST, $_COOKIE and $_FILES. In order to use variables inside your queries you should:
Cast numeric data to integer or float (whichever is appropriate)
Use appropriate escaping to escape other data
A simple example for mysql databases:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . mysql_real_escape_string($name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = '\' OR \'\' =\''
For other database, the escaping practice varies. But generally you're supposed to escape the ' character with '', so:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . str_replace("'", "''", $name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = ''' OR '''' ='''
Having said that, perhaps you might want to switch to PDO. It allows you to use prepared statements, the PDO driver does all the escaping.
The mysql variant came with a method called mysql_real_escape_string, which was appropriate for the version of SQL being targeted. The best thing you can do is write a method to escape the Id. It's important that your escape method is appropriate for the target database. You can also do basic type checking like is_numeric for numeric inputs will reject SQL string injections immediately.
See How to escape strings in SQL Server using PHP?
and follow some of the related links for explicit examples
What's the correct way to code the following
SELECT * FROM table WHERE value = $row['item']
$row['item'] echos correctly, but does not seem to work in the mysql query. Been having this problem for a few days. I've tried .$row['item']. and a few other variations but I must be doing something wrong.
The better more appropriate approach is to use mysqli and prepared statements ie:
$stmt = $mysqli->prepare("SELECT * FROM table WHERE value =?");
$stmt->bind_param("s",$row['item']); // I am assuming row['item'] is a string
$stmt->execute();
If you can't use mysqli or absolutely refuse to you can use this:
$query = "SELECT * FROM table WHERE value = '".mysql_real_escape_string($row['item'])."'";
The answer sort of depends on what is held within the $row['item'] variable. If it's a numeric value, then the query above should be fine. Since it's not working, I assume that the value of that variable is actually a string. In that case, you need to surround the value in quotes so that the database can correctly identify it as a string value (otherwise, it would just be gibberish "commands" that the database can't identify, causing the query to fail).
Regardless of the above, you shouldn't be directly inserting variables into a query under pretty much any circumstances. The reason is that it opens you up to SQL injection if you're not extremely careful. For example, if your $row['item'] variable was wrapped in single quotes in the query, but contained a single quote in its value, then the database would interpret the quote within the variable as the ending quote for the entire parameter, and it would screw up the query. Worse still, a hacker could take advantage of this to end your query entirely, then add a second query of his own making onto it (or they could introduce a UNION query on the end of the original, etc.). At the very least, you should be running something like mysql_real_escape_string() on the variable before using it:
$sql = "SELECT * FROM table WHERE value = " .
mysql_real_escape_string($row['item']);
The best way to get around this and secure your queries is to use prepared statements. These are queries that have placeholders in them instead of concatenated variables. You prepare the query with these placeholders, then you issue additional commands to the database to tell it what values to place in those placeholders. The database then takes care of the tricky issue of sanitizing these variables so that they don't cause any damage. You can use PDO for this:
try {
$dbh = new PDO(DB_DSN,
DB_USER,
DB_PASS,
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
exit();
}
// create query with a named placeholder
$sql = "SELECT * FROM table WHERE value = :value";
try {
$stmt = $dbh->prepare($sql);
// tell PDO to substitute the value in $row['item']
// for the named parameter specified above.
$stmt->bindValue(":value", $row['item']);
// execute the query and load the results into an array
$stmt->execute();
$records = $stmt->fetchAll();
} catch (PDOException $e) {
echo "Query failed: " . $e->getMessage();
exit();
}
foreach ($records as $record) {
// use db records
}
The way I usually recommend doing it is something like this:
$sql = sprintf("SELECT * FROM table WHERE value = '%s'",
mysql_real_escape_string($row['item']));
$item = mysql_real_escape_string($row['item']);
$mysqlQuery = "SELECT * FROM table WHERE value = '" . $item . "'";
you are missing single quotes
SELECT * FROM table WHERE value = '{$row['item']}'
PHP example