Basic PHP sql injection question - php

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?

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

MySQL Injection Confusion, How Does it Actually Protect?

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?)

Do I sanitize/escape correctly?

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

search terms in a PHP query with apostrophes

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

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();
?>

Categories