Security breached via a mysql query which I don't quite understand - php

I've been working on a legacy website. Recently a user has informed us about a potential security breach.
Long story short, when trying to login and using '=' 'or' as password or a username the following query will get executed.
SELECT * FROM `table-goes-here` WHERE `username` = ''=' 'or'' AND `password` = 'some-hash-goes-here'
This query will select everything in that table and will allow login without any actual valid credentials.
I just maintain the site and I have talked to the owner before about such security leaks, he won't listen.
What I want to know is how exactly is this a valid query and what exactly does it do(preferably step by step, explain it to me like I'm 5 version). My MySQL knowledge isn't the best there could be.
I'm very aware that this is an SQL injection. I know how to prepare statements, but they're not in the budget apparently. I just want to know what it does exactly. Namely this part. I have never seen this syntax before and googling doesn't really help as I don't know what I'm looking for exactly.
`username` = ''=' 'or''

Thing is you are actually running
SELECT * FROM table WHERE (username= '' = '') or ('' AND password="");
and (user_username = '' = '') evaluates to true... try
SELECT (username= '' = '') FROM table;
and ('' AND password="") also evaluates to true... try
Select ('' AND password="") FROM table;
also evaluates to true so... everything is shown

I think it's working like this, first of all evals the
username = '' this returns false or 0 then the other parts come
0 = ' '
as you can see from mysql : https://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#operator_equal
if you compare string with 0 (zero), it returns 1 (true)
AS a result:
SELECT 'blabla' = ''; // returns 0
SELECT 0 = ' '; // returns 1

The query would basically evaluate to true and would return all records.
So, the thing which would happen is that the hacker would be logged in as the user whose ID is stored first in your database.
Of course, it wouldn't show the database to the hacker, because you aren't echoing any of the database content anywhere, but with a little modification the hacker can manage to login as any user, not just the first one.
Read following to prevent such attacks How can I prevent SQL injection in PHP?
More info- http://www.w3schools.com/sql/sql_injection.asp
EDIT- The hacker would also have to use a similar trick in password field because else it would become false.

Related

searching datas in the database with prepare statement

a quick question :), I wrote this because someone said that my codes are vulnerable to mysql injection and it is a requirement to learn prepared statement in web programming to avoid any user putting malicious data or statement into the database..What I have is a search function that search data from the database, if you type in a string like this "torres" then i search for torres but if you just put "tor" it won't search for datas that contain "tor" in their name..I don't know the correct format while using prepared statement, If you have advice I'm very happy to take it :)
<?php
if (isset($_POST['search'])) {
$box = $_POST['box'];
$box = preg_replace("#[^0-9a-z]#i","",$box);
$grade =$_POST['grade'];
$section = $_POST['section'];
$strand = $_POST['strand'];
$sql = "SELECT * FROM student WHERE fname LIKE ? or lname LIKE ? or mname LIKE ? or grade = ? or track = ? or section = ?";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $sql)){
echo "SQL FAILED";
}
else {
//bind the parameter place holder
mysqli_stmt_bind_param($stmt, "ssssss",$box, $box, $box, $grade, $strand, $section);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
while($row = mysqli_fetch_assoc($result))
{
echo "<tr>";
echo "<td>".$row['lname']."</td>";
echo "<td>".$row['fname']."</td>";
echo "<td>".$row['mname']."</td>";
echo "<td>".$row['grade']."</td>";
echo "<td>".$row['track']."</td>";
echo "<td>".$row['section']."</td>";
echo "</tr>";
}
}
As requested:
#ArtisticPhoenix I clearly prefer the king's way [compound full text index]. This should be your primary answer showing an example/explaination.
First make a full text index that includes all three fields (this is in PHPmyAdmin, it's a bit easier to explain with an image)
Then do a query like this:
#PDO version SELECT * FROM `temp` WHERE MATCH(fname,mname,lname)AGAINST(:fullname IN BOOLEAN MODE)
#MySqli version SELECT * FROM `temp` WHERE MATCH(fname,mname,lname)AGAINST(? IN BOOLEAN MODE)
SELECT * FROM `temp` WHERE MATCH(fname,mname,lname)AGAINST('edward' IN BOOLEAN MODE)
It seems simple but there are some things with full text to be aware of Min char count which is 3 (I think) anything smaller than that is not searched on. This can be changed but it requires repairing the DB and restarting MySql.
Stop words, these are things like and, the etc. These can also be configured in my.cnf.
Punctuation is ignored. This might not seem a big deal on names but think of hyphenated last names.
Usually I reduce the word min to 2 and point the stopwords to an empty file (disabling them).
The match against syntax is quite different, it's pretty powerful but it's not really used outside of full text. An example is: this is the wild card * and you use '"' double quotes for exact phrase match '"match exactly"', and + is logical AND, such as word+ word+ (default is or), - is do not match this etc... If I remember right, I used it a bunch a few years ago but haven't had to use it recently.
For example doing "begins with" on a partial word
SELECT * FROM `temp` WHERE MATCH(fname,mname,lname)AGAINST('edwar*' IN BOOLEAN MODE)
Same result matches one row. The obvious benefit is searching all 3 fields at the same time, but the full text syntax itself can be quite useful too.
For more information:
https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html
PS. I might add that using OR in a query can really kill performance, I've went as far as to replace simple OR with a UNION because of how bad the performance is on a large table. Logically the DB optimizer has to rescan the entire table for an OR, unlike AND where it can use the result of the previous expression to reduce the next expressions data set (or that is how I understand it). I can say the performance difference is very noticeable using OR vs UNION.
This is true for a compound full text index vs doing OR on each field separately. By default fulltext is faster, but it's even faster this way.
To fix your current query (for the sake of completeness)
You need whats known as an exclusive or, like this:
SELECT * FROM student WHERE ( fname LIKE ? OR lname LIKE ? OR mname LIKE ? ) AND grade = ? AND track = ? AND section = ?
What this does is group the OR's together so that they evalute as one expression to the "next level up" ( outside the parenthesis ). Basically order of operations. In English, you would have to match at least 1 of these columns fname, lname, mname AND you would also have to match all of the rest of the columns as well, to get a result returned for any given row.
If you use all OR (as you are now) and any single field matches, then the query comes back as true with matches. Which is the behaviour you are experiencing now.
If you simply change everything outside of the name fields to AND, Basically remove the parenthesis
Like this:
#this is wrong don't use it.
SELECT * FROM student WHERE fname LIKE ? OR lname LIKE ? OR mname LIKE ? AND grade = ? AND track = ? AND section = ?
Then you have to match this way.
(grade AND track AND section AND mname) OR lname OR fname
So if the last or first name match you get results regardless of any of the other fields. But the mname field you would find has to match with all the rest of the fields to get a result (but you would not likely notice this). Because, it would seem that the query works how you want but only when the mname is a match.
I hope that makes sense. It may be helpful to think of the WHERE clause as an IF condition the same logic rules apply.
Cheers!

MySql Query not run when there is a comment

I am trying to simulate an SQL injection attack on my own database by inserting SQL code into the input spaces.
My SQL code is:
SELECT * FROM Users
WHERE username = '...'
AND password = '...';
where ... is the input.
I am trying to use the following input to select every user and LIMIT that output to 1.
Here is the username input:
' OR ''='
This should get all the usernames.
And here is the password input:
' OR ''='' LIMIT 1; --
This should first get all the passwords, then limit the output to just 1 row, and then comment the final ';
So the code executed should look like:
SELECT * FROM Users
WHERE username = '' OR ''=''
AND password = '' OR ''='' LIMIT 1; -- ';
This does not work for some reason. I am using SequelPro and it tells me it's running 2 queries and not outputting anything. The second query being -- ';
However, if I remove that last part and just run up until the first semicolon, it works and retrieves a single user from the database.
What is going on?
Your query is all right, and the problem you have is from SequelPro which parser appears to be a blunt one, taking a comment for another query.
To avoid this confusion better take out the trailing semicolon from the forged input

Query with quotes not working

I am sending a Query string like:-
String query = "select * from table where id = 12345 or id like '___1435'";
in php code:-
$phone = $_REQUEST['data1'];
$result = mysql_query($phone);
return $result;
through android but it is not working.
But a query like:-
String query = "select * from table where id = 12345 or id = 21435";
is working.
I also tried:-
$phone = mysql_real_escape_string($_REQUEST['data1']);
$result = mysql_query($phone);
return $result;
What could be the problem.
Most probably it is related to the quotes I am using in String.
How can I overcome this ?
Thank You!
A LIKE query needs percentage signs to signify a wild card, which I assume you are trying:
id like '%___1435%'
If you don't want this wildcard behaviour then you could just remove the like
id = '___1435'
I think the problem is that you're attempting to perform a string comparison on an integer field. Try casting your ID field as a CHAR first, like this:
select * from table where id = 12345 or CAST(id as CHAR) like '___1435'
Edit: It's going to be a guessing game unless you can a) post some error logs or b) post your MySQL query log (to see the actual query executing), which you can enable by turning on the General Query Log for your installation. Since it looks like you're accepting your query string via POST, if the string has been encoded in any way (ie url encoding), it'll affect your query if not properly decoded first.
A better and more secure approach would be to create a specific web service that accepts the ID to query, which you can pass into a prepared statement server side. It's better practice anyway so your system isn't vulnerable to wide open queries from a client.

User search query

I am trying to make a query that will take the inputted items and search the database. I know how I have done it several times, but this query just does not want to work.
This is the query
$getcamp = $db->query("SELECT * FROM `campaigns`
WHERE `requirements` LIKE '%$keyw%' OR `description` LIKE '%$keyw%' OR `name`
LIKE '%$keyw%' OR `countries` LIKE '%$country%' OR `id` LIKE '%$camp%' OR `category`
LIKE '%$cat%' AND `active` = '1' ORDER BY `added`")
or die($db->error);
There are a lot of OR breaks, and I have tried wrapping then in parenthesis in several different variations, but the query still will not work, and there are no errors, it just still shows everything in table instead of what was searched. I am not the best with these kinds of queries as normally I am only writing to search 1 or 2 items, this one is searching for a lot.
If I were to only be searching for the first 3 in that statement it will work fine, but once I start adding onto it is when it breaks, even if I put parenthesis around the first 3, I am just not sure what I am doing wrong.
Also just a side note my host doesn't have mysqlnd support that's why I am not using a prepared statement but my variables are escaped before hand.
If one of your variables is empty, it will select all the rows.
You should do the following (this is a basic example so you understand the idea):
$orArray = array();
$orQuery = "";
if (!empty($keyw) && strlen($keyw) > 2) { // and other SQL injection checks
$orArray[] = "`requirements` LIKE '%{$keyw}%' ";
}
if (!empty($orArray())) {
$orQuery = implode(' OR ', $orArray);
}
$query = "SELECT * FROM `campaigns` WHERE ( {$orQuery} ) AND active = 1 ";
// You also should check if $orQuery is empty, if it is, you need to remove AND.
// Well, in that case you wouldn't do the query at all.
Something like this should do, but you need to make the checks for every variable.
PS: I recommend to set a minimum for string search (3 is a good starting point).
PS2: This may not be the problem, but I'm pointing it out because it's a flaw in your scripts logic.
I believe that problem is in fact, that you have "and" after "ors". you should put all ors into extra parenthesis.
To better illustrate consider this php code:
<?php
var_dump(1 || 2 && 0);
var_dump((1 || 2 ) && 0);
and output:
xxx#www0 ~/sp $ php -q a.php
bool(true)
bool(false)
note || is or, && is and

php code, better way of grabbing sql data

I need to grab data from two tables, but I know theres a better, more tidier way to do this. Is it some kind of JOIN i need?
I'll show you my code and you'll see what I mean:
if ($rs[firearm] != "") {
$sql_result2 = mysql_query("SELECT * FROM db_firearms WHERE name='$rs[firearm]'", $db);
$rs2 = mysql_fetch_array($sql_result2);
$sql_result3 = mysql_query("SELECT * FROM items_firearms WHERE player='$id'", $db);
$rs3 = mysql_fetch_array($sql_result3);
if ($rs3[$rs2[shortname]] < 1) {
mysql_query("UPDATE mobsters SET firearm = '' WHERE id ='$id'");
}
}
This question is clear, but your code example has alot of formatting issues and I cannot give you direct answer, based on your example code.
The reason, why your example is unclear, is because.. with what are you going to join the tables? From one table you are selecting by name='$rs[firearm]' and from another by player='$id'. You have to provide the hidden data, like $rs and also $id.
You should definitely read these about mysql join and mysql left join. But I will try to give you an example based on your code, with fixed syntax. (Keep in mind, that I'm no mysql join expert, I did not test this code and also I do not know the joining conditions.) And also, the system structure is unclear.
As I understood, this what your tables do, correct?
mobsters - Users table
items_firearms - Links from users table to items table
db_firearms - Items table
So basically, my example does this: It will have preloaded $rs value, from the users table. It will check, if there is a entry inside the links table and hook the result with them items table. However, if the links table or even the items table can return multiple entries, then this doesn't work and you need to loop your results in much more smarter way.
// I can only assume, that $id is the ID of the player
$id = 2;
// Since I dont know the $rs value, then Im going to make some up
$rs = array(
'id' => 33,
'firearm' => 'famas'
);
if ($rs['firearm']) {
$result = mysql_fetch_array(mysql_query("SELECT ifa.*, dbfa.* FROM `items_firearms` AS `ifa` LEFT JOIN `db_firearms` AS `dbfa` ON `ifa.shortname` = `dbfa.shortname` WHERE `ifa.player` = '$id'"));
if ($result['id']) {
mysql_query("UPDATE `mobsters` SET `firearm` = '' WHERE `id` = '$id'", $db);
}
}
It is pretty clear, that you are new to PHP and mysql.. So I think you should probably edit your question and talk about your higher goal. Briefly mention, what your application are you building..? What are you trying to do with the mysql queries..? Maybe provide the table structure of your mysql tables..? I'm sure, that you will get your questions votes back to normal and also we can help you much better.
NOTES
You have to quote these types of variables: $rs[firearm] -> $rs['firearm']
If you want to check if your $rs['firearm'] equals something, then there is a better way then $rs[firearm] != "". The most simple is if ($rs['firearm']) {echo 'foo';}, but will produce a notice message, when all errors reporting mode. You can use isset() and empty(), but keep in mind, that isset() checks whether the variable has been set.. Meaning, even if its false, then it has been set. empty() reacts to undefined and empty variable the same, without any messages.
Also, "" means NULL, so if you even need to use "", then use NULL instead...much neater way..
I strongly recommend to use mysql class. You can understand the basics behind that idea from this answer. This is gonna make things much more easier for you. Also, mysql class is a must-have when dealing with dynamic applications.
if ($rs3[$rs2[shortname]] < 1) { .. makes no sense.. Do you want to check if the value is empty? Then (simple): if (!$rs3[$rs2[shortname]]) { .. and a very strict standard: if (empty($rs3[$rs2[shortname]])) { ..
Also you have to quote your sql queries, see my examples above.
Is the last mysql query missing $db?

Categories