PDO prepare with named parameters generates a wrong query [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can PHP PDO Statements accept the table name as parameter?
Im writing a script that allows users to register and login to my site. Im using PDO prepare and execute to prevent SQL injections.
Building the query manually like this DOES work:
$a_query = "SELECT COUNT(*) FROM ". $login_table . "
WHERE `username` = ". $my_username . "
AND `password = " . $my_hash ;
$result_1 = $db->prepare($a_query);
$result_1->execute();
But when I try to use prepare correctly like this, it does NOT:
$a_query = "SELECT COUNT(*) FROM :table
WHERE `username` = :name
AND `password = :pass ;"
$result_1 = $db->prepare($a_query);
$result_1->bindParam(":table", $login_table);
$result_1->bindParam(":name", $my_username);
$result_1->bindParam(":pass", $my_hash);
$result_1->execute();
The error message I get from $result_1->errorInfo[2] reads:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax
to use near ''customerlogin' WHERE `username` = 'guest' AND `password`
= 'qwerty' at line 1
As you can see, prepare() mysteriously slices off the first part of the query before sending it to mysql.
Can anyone explain to me why and how I can fix this?

As you can see, prepare() mysteriously slices off the first part of the query before sending it to
mysql.
It does not. It's just the error message handler which actually does that, trying to display only the relevant part of the query. Usually such an error message means "look at the query right before this part". So - it points out at the :table in your query.
Your query does not work because prepared statements do not support identifiers.
PDO, as any other raw API, is insufficient for any real-life query.
A developer should use database abstraction library methods in their application code, not raw API methods.
It will make life much easier and code shorter. For example, your whole code can be made into only 2 lines:
$sql = "SELECT COUNT(*) FROM ?n WHERE `username` = ?s AND `password = ?s";
$data = $db->getOne($sql, $login_table, $my_username, $my_hash);
And it would be safer than your whole screen-long code using raw PDO.

Related

Is there a way to demonstrate SQL injection with mysqli?

I want to make a quick and easy demonstration about how SQL injection work. And I've solved some of my problems. I have a table with random usernames, passwords and emails in, and I'm able to "inject" SQL code to view all of the users in a search with this injection:
' OR '1'='1
This is how my PHP code looks for searching for "members":
if (isset($_POST['search'])) {
$searchterm = $_POST['searchterm'];
echo $searchterm . '<br>';
/* SQL query for searching in database */
$sql = "SELECT username, email FROM Members where username = '$searchterm'";
if ($stmt = $conn->prepare($sql)) {
/* Execute statement */
$stmt->execute();
/* Bind result variables */
$stmt->bind_result($name, $email);
/* Fetch values */
while ($stmt->fetch()) {
echo "Username: " . $name . " E-mail: " . $email . "<br>";
}
}
else {
die($conn->error);
}
}
Now I want to demonstrate some more fatal problems, like someone truncating your whole table. So I tried this code in the search bar:
'; TRUNCATE TABLE Members; --
But I get this error message:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'TRUNCATE TABLE Members; -- '' at line 1
It seems like I get an extra ', but I don't know how to get rid of it, though the -- would comment that out. First I thought that the problem was that I had no whitespace behind the -- but adding a whitespace didn't make any difference.
I have tried switching to PDO, because I thought there was a problem with mysqli not accepting multiple queries, but then I somewhere read that PDO doesn't support that either, but I don't know.
Is there a way I can make it work?
I later found that PDO supports multi-querying by default, but when I tried it it didn't work. Maybe I bound the parameters wrong. But I couldn't even make a simple select query to work.
mysqli_query() does not support multi-query by default. It has a separate function for that: mysqli_multi_query().
SQL injection is not only about running multiple statements, the famous XKCD cartoon notwithstanding.
Your code has a bad SQL injection vulnerability. Do you think that using prepare() somehow makes a query safe, even though you interpolate content from your $_POST request data directly into the SQL string?
Your code is this:
$searchterm = $_POST['searchterm'];
$sql = "SELECT username, email FROM Members where username = '$searchterm'";
if ($stmt = $conn->prepare($sql)) {
/* execute statement */
$stmt->execute();
...
It's easy for unsafe input to make SQL injection mischief this way. It might even be innocent, but still result in problems. Suppose for example the search is: O'Reilly. Copying that value directly into your SQL would result in a query like this:
SELECT username, email FROM Members where username = 'O'Reilly'
See the mismatched ' quotes? This won't do anything malicious, but it'll just cause the query to fail, because unbalanced quotes create a syntax error.
Using prepare() doesn't fix accidental syntax errors, nor does it protect against copying malicious content that modifies the query syntax.
To protect against both accidental and malicious SQL injection, you should use bound parameters like this:
$searchterm = $_POST['searchterm'];
$sql = "SELECT username, email FROM Members where username = ?";
if ($stmt = $conn->prepare($sql)) {
$stmt->bind_param('s', $searchterm);
/* execute statement */
$stmt->execute();
...
Bound parameters are not copied into the SQL query. They are sent to the database server separately, and never combined with the query until after it has been parsed, and therefore it can't cause problems with the syntax.
As for your question about mysqli::query(), you may use that if your SQL query needs no bound parameters.
Re your comment:
... vulnerable to injection, so I can show the students how much harm a malicious attack may [do].
Here's an example:
A few years ago I was an SQL trainer, and during one of my trainings at a company I was talking about SQL injection. One of the attendees said, "ok, show me an SQL injection attack." He handed me his laptop. The browser was open to a login screen for his site (it was just his testing site, not the real production site). The login form was simple with just fields for username and password.
I had never seen his code that handles the login form, but I assumed the form was handled by some code like most insecure websites are:
$user = $_POST['user'];
$password = $_POST['password'];
$sql = "SELECT * FROM accounts WHERE user = '$user' AND password = '$password'";
// execute this query.
// if it returns more than zero rows, then the user and password
// entered into the form match an account's credentials, and the
// client should be logged in.
(This was my educated guess at his code, I had still not seen the code.)
It took me 5 seconds to think about the logic, and I typed a boolean expression into the login form for the username, and for the password, I typed random garbage characters.
I was then logged into his account — without knowing or even attempting to guess his password.
I won't give the exact boolean expression I used, but if you understand basic boolean operator precedence covered in any Discrete Math class, you should be able to figure it out.
Did you try something like this ?
'(here put something);
in this way you are going to close the query with ' and add other stuff to it, when you add ; everything else is going to be discarded

PHP Comparing Emails

I am trying to compare an Email address stored in a database to one entered in the input however it is not recognizing:
I am trying to select a colum from my db with:
$query = mysql_query("SELECT * FROM `men` WHERE `Email`=$user_email");
however the query returns 0 even though the emails are exactly the same. What is the issue here
There are many issues. This query will interpolate to Email = email#example.com which is a syntax error in MySQL.
You should be checking for errors after mysql_query with something like mysql_query($query) or echo mysql_error()
You need to wrap $user_email in quotes so it interpolates to Email = 'email#example.com', which is the valid/desired query.
You should not even be using ext/mysql at all; it is deprecated. See big pink box.
Your code is vulnerable to injection because the query is not properly parameterized.
A much better alternative would be (in PDO):
$query = $pdo->prepare("SELECT * FROM `men` WHERE `Email` = ?");
$query->execute(array($user_email));
$result = $query->fetch();
Note that my comments about error checking and parameterization still apply to PDO.

Secure against SQL Injection - PDO, mysqli [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Best way to prevent SQL Injection in PHP
I just found that my website is vunerable.
Since it's connected to a DB and have functions like: Register, Change Password, Notices, etc... and SUPOSING it's fully vulnerable.
What should I look for into the code in order to start making it safe?
I mean, I did some researches and everywhere, everyone says different things about security.
"Use PDO."
"Use mysql_real_escape_string."
"Use addslashes."
What exactly should I look for??
"$_POST" and "$_GET" variables??
"$_SESSION" variables?
SQL querys?
$sql = "select * from user";
$sql = "update user set user="new_user_name";
$sql = "insert into user (user) values ('userid')";
What should I do in each case?
Please, help me to know what and where I must go.
Thank you.
Following are the points to be considered for making safe php application.
USE PDO or mysqli
Never trust any inputs. Consider every variable viz $_POST, $_GET, $_COOKIE, $_SESSION, $_SERVER as if they were tainted. Use appropriate filtering measure for these variables.
To avoid XSS attack use php’s builtin functions htmlentities,
strip_tags, etc while inserting the user input data into the
database.
Disable Register Globals in PHP.INI
Disable “allow_url_fopen” in PHP.INI
Don’t allow user to input more data than required. Validate input to
allow max number of characters. Also validate each field for
relevant datatypes.
Disable error reporting after Development period. It might give
information about database that’ll be useful to hackers.
Use one time token while posting a form. If token exist and matches
the form post is valid otherwise invalid.
Use parametrized database queries
Use stored procedures
You can google for each point for more details.
HOpe this helps
What you should look for: Any data send from the client/user. Sanitize/escape this data.
PDO can sanitize queries (using PDO::prepare) and supports multiple SQL systems.
For MySQL, use MySQLi. mysqli_real_escape_string is the function to use for sanitizing data if you are using MySQL.
None of the SQL queries you provided are actually vulnerable to SQL injection.
SQL injection vulnerabilities happen because SQL input is not properly escaped.
For example:
$sql = "select * from users where user_id =" . $_GET['user_id'];
Consider if I passed in the following:
http://some_server.com/some_page.php?user_id=123%20or%201=1
The query when executed would end up being:
select * from users where user_id = 123 or 1=1
To fix this, use parameterized queries:
$query = "select * from users where user_id = ?"
When you bind the user_id value to the query, the data access layer will escape the input string properly and the following would be executed:
select * from users where user_id = '123 or 1=1' which would not return any rows, preventing the injection
If using PHP and the mysql extension:
$sql = "select * from users where user_id = '" . mysql_real_escape_string($_GET['user_id']) . "'";
Keep in mind you need to escape ALL input that is going into a SQL query:
$sql = "select id_column from some_table where id = 1";
$stmt = mysqli_query($conn, $sql);
if($stmt === false) die(mysqli_error($conn) . "\n");
while($row = mysqli_fetch_assoc($conn, $stmt) {
$sql = "update some_other_table set some_value = 'new value' where some_column = '" . mysqli_real_escape_string($conn, $row['id_column']) . "'";
....
}
This is because values you select from the database might include characters that are not safe for execution in a SQL statement, like the name "O'Hara" or example.
}
I've been using PDO.
An example for that in your case:
<?php
$stmt = $dbh->prepare("insert into user (user) values (?)");
$stmt->bindParam(1, $name);
$name = 'ValueHere';
$stmt->execute();
?>

SQL query escaping + codeigniter

I'm using codeigniter and most of the time use active record for my queries (which automatically escapes them), but this query doesn't seem to fit neatly into it because of the variable. So I need to figure out how to escape the query manually.
Codeigniter docs suggest escaping the queries this way:
$sql = "INSERT INTO table (title) VALUES(".$this->db->escape($title).")";
My original query
$sql = "SELECT * FROM (`user_language`) WHERE `user_id` = '{$id}'";
My escaped query
$sql = "SELECT * FROM (`user_language`) WHERE `user_id` = '{$id}' VALUES(".$this->db->escape($user_language).")";
But I'm having trouble getting the syntax right. Error messages are:
PHP error message: Undefined variable: user_language
SQL error: syntax wrong...near 'VALUES(NULL)' at line 1
$sql = "SELECT * FROM `user_language` WHERE `user_id` = " . $this->db->escape($id);
if you want to select the language of the user given by $id it should work that way.
dealing with numbers an alternative would be:
$sql = "SELECT * FROM `user_language` WHERE `user_id` = " . (int)$id;
codeigniter does also support prepared statements as "query bindings":
The secondary benefit of using binds is that the values are
automatically escaped, producing safer queries. You don't have to
remember to manually escape data; the engine does it automatically for
you.
I'm confused why you say you cannot use the Active Record class with CI, this is a simple SQL call (example below uses method chaining):
$this->db->select('*')->from('user_language')->where('user_id', $id);
$query = $this->db->get();
Your $id is then escaped properly, and you mitigate any injection.
Personally I use AR whenever possible, it allows me to write quick efficient code, and not worry about the bad things with SQL calls (custom queries).

Make this query safe? [duplicate]

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
Best way to stop SQL Injection in PHP
I have seen some of examples that use something called a PDO to make a query safe from sql-infection, or others that use real_escape, but they all seem to be incomplete or assume some knowledge. So I ask, take this simple update query and make it safe from sql-injection.
function updateUserName($id,$first,$last)
{
$qry = 'UPDATE user SET first = "'.$first.'", last = "'.$last.'" WHERE id = '.$id;
mysql_query($qry) or die(mysql_error());
}
Basically, you have to :
escape string, with mysql_real_escape_string
make sure integer really are integers ; for instance, using intval.
Which, in your specific case, would give something like this :
$qry = 'UPDATE user SET first = "'
. mysql_real_escape_string($first)
. ' ", last = "'
. mysql_real_escape_string($last)
. '" WHERE id = '
. intval($id);
Of course, this is considering that last and first are varchar, and that id is an integer.
As a sidenote : when an SQL error (this is also true for whatever kind of error you can thing about) occurs, you should not display a technical error message and just let the script die.
Your users will not understand that technical error message -- they won't know what to do with it, and it's not their problem.
Instead, you should log (to a file, for instance) that technical error message, for your own usage ; and display a nice "oops an error occured" page to the user.
This is the better query:
$qry = 'UPDATE user SET first = "'.mysql_real_escape_string($first).'", last = "'.mysql_real_escape_string($last).'" WHERE id = '.intval($id);
Use mysql_real_escape_string for strings and intval for numbers in your queries to make them safer.
mysql_real_escape_string + sprintf
$qry = sprintf('UPDATE user SET first = '%s', last = '%s' WHERE id = %d', mysql_real_escape_string($first), mysql_real_escape_string($last), $id);
I like it that way.
You could wrap all your variables in mysql_real_escape_string(). PDO (PHP Data Objects) is a better solution though, if it's available in your environment. You can find these docs here.
PDO will make your code more Object-Oriented and automate some of these tasks for you. Some good sample code of PDO preparing statements can be found deeper into the docs here.

Categories