MYSQL query magically grabs numeric value - php

I have the following query
SELECT * FROM (`user_profiles`) WHERE `user_id` = $user_id LIMIT 1
$user_id is a URI segment. For instance $user_id = 64 would produce
SELECT * FROM (`user_profiles`) WHERE `user_id` = '64' LIMIT 1
If I add alphabetical characters to the user id, e.g. http://www.mysite.com/profile/64kjdsg
I get:
SELECT * FROM (`user_profiles`) WHERE `user_id` = '64kjdsg' LIMIT 1
which still returns the correct data although there is no user id equal to 64kjdsg. The user id column in my table is int(11). The query seems to automatically grab the numeric value from 64kjdsg and match that in the db table. Is this a MYSQL function I'm not aware of?
How is this happening? I'm querying using the Codeigniter framework if that makes ant difference.
UPDATE: found a similar question MySQL integer comparison ignores trailing alpha characters

As you are comparing to a numeric column, MySQL casts your string to a number (so it removes everything from the occurance of the first non-number character). It's its default behavior:
mysql> select '23andthensome' + 4;
+---------------------+
| '23andthensome' + 4 |
+---------------------+
| 27 |
+---------------------+
1 row in set, 1 warning (0.02 sec)
mysql> show warnings;
+---------+------+---------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '23andthensome' |
+---------+------+---------------------------------------------------+
1 row in set (0.02 sec
So, make more proper queries, check beforehand wether something is a number (filter_var(FILTER_VALIDATE_INT,$id);), only use it when it is, and then: don't send it as a string to MySQL: if you want to compare numbers, send the number, which should not be quoted.
Alternatively, you can let MySQL do the work, but it seems a waste:
mysql> select 23 = '23andthensome';
+----------------------+
| 23 = '23andthensome' |
+----------------------+
| 1 |
+----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select cast(23 as CHAR) = '23andthensome';
+-------------------------------------+
| cast(23 as CHAR) = '23andthensome' |
+-------------------------------------+
| 0 |
+-------------------------------------+
1 row in set (0.02 sec)

Check in your script is urlsegment integer. You can use ctype_digit to do it. If isn't, don't touch your db. Say "No such user"

Check this out: http://ideone.com/khpEv, it is called type juggling. If you '64kjdsg' string will be converted to integer (64) because user_id is INT else it will produce syntax error.
PHP example:
<?php
echo (int) '64kjdsg'; // 64
?>

this is security hole
however, if
$user_id = 5;
result is
SELECT * FROM (`user_profiles`) WHERE `user_id` = 5 LIMIT 1
not
SELECT * FROM (`user_profiles`) WHERE `user_id` = `5` LIMIT 1
try to use intval()
$user_id = intval($user_id);

Related

SELECT * FROM table WHERE value IN array only return one result

I'm trying to get all posts from a table where the id is in an array.
So, I have an array with 2 ids and the 2 ids have a post, but the query only returns the post from the first id.
This is my code with sample values:
$ides= array();
array_push($ides, '1, 2');
$ids = implode(',',$ides);
$sql="SELECT * FROM post WHERE id IN ('$ids') ORDER BY date DESC";
Then, in this case, the result that I have is only the post where the id is "1". What am I doing wrong?
Your query returns only one result because you have enclosed both values into the single quotes. So your query looks like this
SELECT * FROM post WHERE id IN ('1, 2') ORDER BY date DESC
And it should look like this
SELECT * FROM post WHERE id IN (1,2) ORDER BY date DESC
If you run your initial query in mysql console, it'll probably show you a warning
mysql> select cast('1,2' as unsigned);
+-------------------------+
| cast('1,2' as unsigned) |
+-------------------------+
| 1 |
+-------------------------+
1 row in set, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------+
| Warning | 1292 | Truncated incorrect INTEGER value: '1,2' |
+---------+------+------------------------------------------+
1 row in set (0.00 sec)
mysql>
To fix it, I suggest the following code
$ids = [1,2];
$ids_string = implode(',',$ids);
$sql="SELECT * FROM post WHERE id IN ($ids_string) ORDER BY date DESC";

PDO query binding string instead of integer returns results

I have a table with and auto increment id and was just testing a few scenarios when I stumbled across a problem whereby PDO or mysql seems to convert a string to an integer when in an array. Does anyone know why?
If my query is as follows:
$check = $db->prepare("SELECT * FROM tbl_test WHERE id=:id");
$check->execute(array(':id'=>1));
it retrieves 1 record - all fine, but if the query uses a string either by design or mistake as follows:
$check = $db->prepare("SELECT * FROM tbl_test WHERE id=:id");
$check->execute(array(':id'=>'1 OR id > 0'));
it still retrieves a record with id=1.
Surely nothing should be found? I appreciate I should never allow the 2nd scenario to happen but why is PDO / mysql converting the string to an integer and how is it doing it?
This is a MySQL bug/oversight in string to integer conversion. Instead of raising an error when given an incorrect integer literal, it simply issues a warning.
mysql> select '1'+0;
+-------+
| '1'+0 |
+-------+
| 1 |
+-------+
1 row in set (0,00 sec)
mysql> select '1 hello world'+0;
+-------------------+
| '1 hello world'+0 |
+-------------------+
| 1 |
+-------------------+
1 row in set, 1 warning (0,00 sec)
mysql> show warnings;
+---------+------+---------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '1 hello world' |
+---------+------+---------------------------------------------------+
1 row in set (0,00 sec)
For good or bad, that's how MySQL is designed to behave:
mysql> SELECT CASE
-> WHEN 123='123 pink elephants' THEN 'Equal'
-> ELSE 'Different' END
-> AS 'How are they?';
+---------------+
| How are they? |
+---------------+
| Equal |
+---------------+
1 row in set, 1 warning (0.00 sec)
As you can see, though, it triggers a warning:
mysql> SHOW WARNINGS;
+---------+------+--------------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '123 pink elephants' |
+---------+------+--------------------------------------------------------+
1 row in set (0.00 sec)
It's because execute create somthing like: SELECT * FROM tbl_test WHERE id='1 OR id > 0'
$check = $db->prepare("SELECT * FROM tbl_test WHERE id=:id OR id>:id2");
$check->execute(array(':id'=>'1', ':id2' => 0));
or just
$check = $db->prepare("SELECT * FROM tbl_test WHERE id>:id");
$check->execute(array(':id'=>'0'));
With prepared statements and placeholders, the database knows to expect a value that suits the column type. I would expect that it sees your numeric id column and casts the '1 or id > 0' to a number - so you just get the 1.

Why does ""=" exploit this MySQL Query?

On a MySQL 5.6 database, I build this simple table and insert a row:
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
INSERT INTO users VALUES ('bob', 'pass');
Then I set up a query in PHP like so:
$query = "SELECT * from users where username=\"".$username."\" and password=\"".$password."\"";
When $username and $password are both equal to ""=", the resulting query is SELECT * from users where username="""="" and password="""="". When that's used to query the table set up before, the row in the table is returned.
The question is, how is MySQL evaluating that query such that it considers the query valid and that the WHERE statement is true? Assuming all double-quotes are matched with the nearest untaken adjacent double-quote, I would have expected the query to be interpreted something like this, which looks like it should be considered gibberish:
SELECT * from users where username=""
"="
" and password="
""
=
""
Here's an example of this behavior on a MySQL 5.6 DB: http://sqlfiddle.com/#!9/02e606/2
It's because MySQL allows "" as an alternative for \".
mysql> select '"foo"' = """foo""", '"foo"' = "\"foo\"", 'foo' = """foo""";
+---------------------+---------------------+-------------------+
| '"foo"' = """foo""" | '"foo"' = "\"foo\"" | 'foo' = """foo""" |
+---------------------+---------------------+-------------------+
| 1 | 1 | 0 |
+---------------------+---------------------+-------------------+
1 row in set (0.00 sec)
In your specific case:
SELECT * from users where username="""="" and password="""=""
would be the same as (if I'm parsing this correctly in my head):
SELECT * from users where (username='"="" and passsword="') = ""
A three-way equality test IS syntactically correct, but does not evaluate as expected
mysql> select 'a' = 'a' = 'a';
+-----------------+
| 'a' = 'a' = 'a' |
+-----------------+
| 0 |
+-----------------+
because that parses as (a=a)=a -> true=a -> false
--
comment follow up for #juan:
mysql> select 'a'='a'='a', 'a'='a'='b', 'a'='b'='a', 'b'='a'='a', 'b'='b'='a';
+-------------+-------------+-------------+-------------+-------------+
| 'a'='a'='a' | 'a'='a'='b' | 'a'='b'='a' | 'b'='a'='a' | 'b'='b'='a' |
+-------------+-------------+-------------+-------------+-------------+
| 0 | 0 | 1 | 1 | 0 |
+-------------+-------------+-------------+-------------+-------------+
It's non-intuitive, because
mysql> select 'a'=('a'='b'), ('a'='a')='b', true='b', 'a'=false;
+---------------+---------------+----------+-----------+
| 'a'=('a'='b') | ('a'='a')='b' | true='b' | 'a'=false |
+---------------+---------------+----------+-----------+
| 1 | 0 | 0 | 1 |
+---------------+---------------+----------+-----------+
--- followup to the followup: again, your original query:
SELECT * from users where username="""="" and password="""=""
will run as
SELECT * from users where (username='"="" and passsword="') = ""
SELECT * from users where (false) = ""
SELECT * from users where true
because false ="" in mysql evaluates to TRUE, therefore ALL rows get included, unless you have a user whose username is literally "="" and password=".
I guess you are doing something like this
Sql Demo
SELECT """="" and password="""="" -- this is equal to 0
from users
where (username = "anything") = false;
In MYSQL you can escape single and double quotes like this:
Instead of \" you can do this "" when useing " as field wrapper.
Same with \' becomes '' when using ' as field wrapper
And what in MYSQL also work is:
SELECT * FROM tbl WHERE name = 'ab' 'b c'
real query:
SELECT * FROM tbl WHERE name = 'abb c'
See Online MYSQL Documention

Update MySQL Query using substring of a value type in database

I am in need of a query that do something like this
UPDATE myTableName SET status = 0 WHERE type ( type contains last character = x)
E.g type values can be
ABCD9238
ADA323S
It needs to detect the second value.
You can use the right function for this as
mysql> select right('ABCD9238', 1);
+----------------------+
| right('ABCD9238', 1) |
+----------------------+
| 8 |
+----------------------+
1 row in set (0.00 sec)
mysql> select right('ADA323S', 1);
+---------------------+
| right('ADA323S', 1) |
+---------------------+
| S |
+---------------------+
1 row in set (0.00 sec)
So if you are looking for last character to be S then you can use as
UPDATE myTableName SET status = 0 WHERE right(type,1) = 'S'
You could use Substring function
http://www.w3resource.com/mysql/string-functions/mysql-substring-function.php
Second character from the end
UPDATE myTableName SET status = 0 WHERE substring(type,-2,1) = x
The last character
UPDATE myTableName SET status = 0 WHERE substring(type,-1) = x
If I understand your requirements, you need to know if a character is in a column "type".
If so you can use LIKE statement.
UPDATE myTableName SET status = 0 WHERE type LIKE "%CHARACTER_YOU_WANT_TO_CHECK%"
If the character is in the last position you should remove the last "%"
It can be used for strings, not only for char's.

Mysql query where column_name=a the same as column_name=0?

When i am running a query, for example:
"SELECT * FROM program_detaljer where program_ref_id='a'"
All I get from the result is program_ref_id = 0.
Why does this happen, when the program_ref_id column is supposed to be int(11)?
You should read about types conversion in mysql
Before comparison mysql tries to convert string ('a') to number ('0').
mysql> SELECT 0='a';
+-------+
| 0='a' |
+-------+
| 1 |
+-------+
1 row in set, 1 warning (0.00 sec)
You could check value before quering. Or replace = with LIKE : ). (But better to check value type before.)
mysql> SELECT 0 LIKE 'a';
+------------+
| 0 LIKE 'a' |
+------------+
| 0 |
+------------+
1 row in set (0.00 sec)

Categories