Why this sql is correct? (sql injection) - mysql

What does it mean?
SELECT * from users where password = ''*'';
if I check this in mysql workbench I get only one line, although I have lot of users in table.
What exactly does this select?

Interesting question. Let's see what ''*'' does.
mysql> select ''*'';
+-------+
| ''*'' |
+-------+
| 0 |
+-------+
Let's create some users:
mysql> select * from users;
+------+-------+
| id | name |
+------+-------+
| 1 | joe |
| 2 | moe |
| 3 | shmoe |
| 4 | 4four |
+------+-------+
And test our query:
mysql> select * from users where name = ''*'';
+------+-------+
| id | name |
+------+-------+
| 1 | joe |
| 2 | moe |
| 3 | shmoe |
+------+-------+
Interestingly enough, user 4 was not selected! But let's try this way:
mysql> select * from users where name = 4;
+------+-------+
| id | name |
+------+-------+
| 4 | 4four |
+------+-------+
So, what can we deduct from this?
''*'' somehow means 0 (I am not that fluent in mysql string operators, so let's take it as a fact);
MySQL, apparently, does type conversions in this case. So if you query a varchar column against an integer, it tries to convert those strings to ints and see if it's a match;
You have only one row whose password begins with 0 or non-digit.

You can always use an expression in SQL. Like SELECT 5-4 AS one and get 1. So you can tell that here is an expression.
MySQL is a loosely typed language, so it can multiply strings. Casting them to numbers. And get you zero as a result of '' * ''
When comparing a string with a number, MySQL casts both to a number. So 0 = 'name' condition will get you true

The ''*'' is a multiplication: its two arguments (empty strings) are converted to numericals (i.e. 0) and the result is 0. Then the left side of the equation is also converted to a number, which will sometimes be zero (when the password cannot be evaluated to a non-zero number), sometimes not.
It is a bit obscure, and you could ask yourself whether this was intended in your case or an accidental behaviour, while the actual intention was to test for '*'. A user with bad intentions might have entered '*' as a password hoping you were not protected against SQL injection in order to get into the system without a valid password.

Related

MS Access - Data Type Mismatch in Criteria Expression

Using the query grid , comparing a String field with a Replace function result of another String field (same table) results in a Data Type Mismatch error when trying to filter for ‘Not Like’ (or <>).
‘TypeName’ confirms that all records are of type “String”.
The problem is caused by “MyStrCalc: Replace([StrA],".","_")” which is compared with StrB. StrA contains Null for some records. These are filtered out (Criterium = “Is Not Null”). But even when creating a new query that uses the result of the first, the same error occurs. I have also tried Nz.
If I use Make Table to create a new table where StrA “Is Not Null” and run effectively the same query, there’s no issue.
The data in the table changes frequently, so having to create a separate table every time (tens of thousands of records) is a real nuisance.
Any suggestions how to make the query work would be greatly appreciated.
(By the way – the version used is MS Access 2019 under Windows 10, both with latest updates.)
Thank you for your much appreciated quick reply.
I tried a few things as detailed below with the fourth attempt providing the desired result.
Source table t1:
| UID | StrA | StrB |
| ---:| ----- | ----- |
| 1 | Str.1 | Str_1 |
| 2 | | Str_2 |
| 3 | Str.3 | Str_4 |
Desired Result = StrA<>StrB after replacing dots in StrA with underscores:
| UID | StrA | StrB
| ---:| ----- | -----
| 2 | | Str_2
| 3 | Str.3 | Str_4
q1_Bad:
SELECT t1.UID, t1.StrA, t1.StrB, Replace([StrA],".","_",1,-1,1) AS StrACalc
FROM t1
WHERE (((Replace([StrA],".","_",1,-1,1)) Not Like [StrB]));
Result: “Data type mismatch in criteria expression”.
q2_Runs_CannotFilter:
SELECT t1.UID, t1.StrA, t1.StrB, Replace([StrA],".","_",1,-1,1) AS StrACalc, [StrACalc] Not Like [StrB] AS StrACalc_NtEq_StrB
FROM t1
WHERE (((t1.StrA) Is Not Null));
Result: Runs, but filtering field ‘StrACalc_NtEq_StrB’ (SQL or after running query) results in “Data type mismatch in criteria expression”.
q3_OK_SQL_FilterFail:
SELECT t1.UID, t1.StrA, t1.StrB, Replace(Nz([StrA]),".","_",1,-1,1) AS StrACalc, Nz([StrACalc] Not Like [StrB]) AS StrACalc_NtEq_StrB
FROM t1;
Result: Runs, but filtering field ‘StrACalc_NtEq_StrB’ is only possible after running query. Adding “Nz([StrACalc] Not Like [StrB]) AS StrACalc_NtEq_StrB” results in “Enter Parameter Value | StrACalc”.
Note: If the result of the above is called in another query, the SQL filtering will work.
q4_OK
SELECT t1.UID, t1.StrA, t1.StrB
FROM t1
WHERE (t1.StrB) Not Like Replace(Nz([StrA]),".","_",1,-1,1);
Finally – Desired result:
| UID | StrA | StrB |
| ---:| ----- | ----- |
| 2 | | Str_2 |
| 3 | Str.3 | Str_4 |

What is the proper way of checking for falsyness of trimmed strings in MySQL queries?

Let's assume that I have a table called users, with three columns - id, name, and nickname.
+----+---------+----------------+
| id | name | nickname |
+----+---------+----------------+
| 1 | Alice | Allie |
| 2 | Bob | B |
| 3 | Charles | Charlie |
+----+---------+----------------+
I want to display the concatenated name and nickname (both trimmed), if the nickname is not, when trimmed, an empty string. If the trimmed nickname is empty, then only the name should be displayed.
I've tried doing this:
SELECT
users.id,
CONCAT(
TRIM(users.name),
IF(
TRIM(users.nickname),
CONCAT(' - ',TRIM(users.nickname)),
''
)
)
FROM users
expecting it to return this
Alice Allie
Bob B
Charles Charlie
However, only the names are returned, and the nicknames are not. What am I doing wrong? The appropriate fiddle can be found here.
In MySQL, the truth/false of a string is the same as whether you can cast the string to an integer. If the string casts to the integer 0, it is "false". If the string casts to anything besides 0, it is "true".
Casting a string to an integer in MySQL means to read any leading digits and use the numeric value. If there are no leading digits, assume 0, which makes it "false".
mysql> select 'abc123' + 0;
+--------------+
| 'abc123' + 0 |
+--------------+
| 0 |
+--------------+
mysql> select '123abc' + 0;
+--------------+
| '123abc' + 0 |
+--------------+
| 123 |
+--------------+
In your case, you are testing IF(TRIM(users.nickname), <true-expr>, <false-expr>) but it's very likely that the nicknames do not start with digits (unless the user is a rap artist :-), so they will all be cast as 0, so they will result in the "false" expression.
If you're testing for the length of the string, you should do that:
IF(LENGTH(TRIM(user.nickname) > 0, <true-expr>, <false-expr>)
The answer from #SalmanA is a good alternative method to produce the result you want, but I am writing this answer to explain why you saw the behavior you saw.
CONCAT_WS should do the trick. It skips NULL values but you explicitly need to handle empty strings:
SELECT CONCAT_WS('-'
, NULLIF(TRIM(users.name), '')
, NULLIF(TRIM(users.nickname), ''))
FROM users

mysql query showing wrong result

I have a database table look like this
+======+===========+============+
| ID | user Name |user surname|
+======+===========+============+
| 100 | name | surname |
| 101 | name | surname |
| 102 | name | surname |
+===============================+
When i run this query which should show me no rows because there is no row with 101foo2 value :
SELECT * FROM tableName WHERE ID = '101foo2'
I am getting a result with same ID without the foo2 word
+======+===========+============+
| ID | user Name |user surname|
+======+===========+============+
| 101 | name | surname |
+===============================+
how it is showing the row with ID 101 if my query is ID = '101foo2'
You are mixing types. ID is an integer (or number). You are comparing it to a string. So, MySQL needs to decide what type to use for the comparison. What types gets used? Well, a string? No. A number. The string is converted to a number, using the leading digits. So, it becomes 101 and matches.
You should really only compare numbers to numbers, and strings to strings. You could try to write the code as:
SELECT * FROM tableName WHERE ID = 101foo2
However, you would get an error. Another possibility is to force the conversion to a string:
SELECT * FROM tableName WHERE CAST(ID as CHAR) = '101foo2'

MySQL Query results

Table name : Students.
The Table i have:
mysql> SELECT * from Students;
+-----------+-------------+-------+
| Rollno | Name | Marks |
+-----------+-------------+-------+
| 251602122 | Sumit Tyagi | 70 |
| 251602121 | parveen | 90 |
+-----------+-------------+-------+
Following query returns the following result even 8 is not a attribute.
mysql> select 8 from Students;
+---+
| 8 |
+---+
| 8 |
| 8 |
+---+
Similarly
mysql> SELECT 'some_string' from Students;
+-------------+
| some_string |
+-------------+
| some_string |
| some_string |
I just want to know why this happens.
The query returns one line for every record in your table.
But you don't select data from those record. You just select the number 8 for each line. And this gets returned.
Select statement looks for column name in a table. You can make sure SQL look for a column name in a table by using TableName.ColumnName.
In the example you wrote, you are asking for a constant or hardcoded value 8/some_string to be returned from the table which is not the column name. So it will return the hardcoded or constant value you asked for, the number of times equal to number of rows in your table.
If you want to make sure it look for the column name, use the syntax I mentioned as TableName.ColumnName. You can also provide an alias for your table. So in the example above, if you use the syntax as
SELECT Students.8 from Students;
or
SELECT s.8 FROM Students s;
It will look for column name as 8 instead of constant or hardcoded value 8.
If I am not wrong, it is a best practice to use TableName.ColumnName or alias.ColumnName while writing queries as it checks for column name in that particular table.

How to get MySQL command line tool to show booleans stored as BIT sensibly by default

I got a problem with selecting boolean types stored as BIT with MySQL. I know that I can get bit values shown in a sensible with with custom queries like with SELECT CAST(1=1 AS SIGNED INTEGER) or with SELECT BOOLFIELD + 0 ...
However, is there any way to get our booleans shown in a sensible way with command line client with queries like SELECT * FROM TABLE ?
UPDATE : At the moment I see only space in the results Example:
mysql> SELECT distinct foo, foo + 0 from table
+------+-------+
| foo | foo_0 |
+------+-------+
| | 0 | <-- Only space
| | 1 | <-- Space, one space less
+------+-------+
With some googling, I found some (maybe related) bugs from MySQL bug DB (http://bugs.mysql.com/bug.php?id=28422, http://bugs.mysql.com/bug.php?id=43670) but not answer or fix?
To store booleans, one really ought to use MySQL's BOOLEAN type (which is an alias for TINYINT(1), given that MySQL doesn't have real boolean types): 0 represents false and non-zero represents true.
Whilst it might feel like storing a boolean in a byte is more wasteful than in a BIT(1) column, one must remember that a few saved bits will translate into more bit operations for the CPU on data storage & retrieval; and I'm unsure whether most storage engines pad BIT columns to the next byte boundary anyway.
If you insist on using BIT type columns, you should be aware that they are returned as binary strings. The MySQL command line client (stupidly) attempts to render binary strings as textual (by applying its default character set), which is what causes the behaviour that you observe—there's no way to avoid this (other than to manipulate the field in the select list in order that it as returned as something other than a binary string, as you are already doing).
However, if you also insist on using SELECT * (which is bad practice, albeit somewhat more understandable from the command line client), you might consider defining a view in which the manipulation is performed and then SELECT from that. For example:
CREATE VIEW my_view AS SELECT foo + 0 AS foo, bar FROM my_table;
Then one could do:
SELECT * FROM my_view WHERE foo = 1 AND bar = 'wibble';
A BIT ugly, but maybe some workaround: CASE WHEN ... THEN ... END
Instead of
> select
guid,
consumed,
confirmed
from Account
where customerId = 'xxxx48' and name between xxxx and xxxx;
+--------------------------------------+----------+-----------+
| guid | consumed | confirmed |
+--------------------------------------+----------+-----------+
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | | |
+--------------------------------------+----------+-----------+
One could do:
> select
guid,
case when consumed then '1' when not consumed then '0' end as been_consumed,
case when confirmed then '1' when not confirmed then '0' end as been_confirmed
from Account
where customerId = 'xxxx48' and name between xxxx and xxxx;
+--------------------------------------+---------------+----------------+
| guid | been_consumed | been_confirmed |
+--------------------------------------+---------------+----------------+
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 1 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 1 |
| xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 1 | 0 |
+--------------------------------------+---------------+----------------+