I'm auditing a project and I found a way to inject data in a query.
The project uses Hibernate and for this piece of code Session.createSqlQuery() and then a .list()
The SQL is something like : "SELECT * FROM tablename ORDER BY column XXXXXX"
XXXXXX can be modified using Fiddler. So I tried
SELECT * FROM tablename ORDER BY column DESC; truncate table tablename;
Unfortunately (well only for my injection attempt) it's not working and I'm getting :
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 'truncate table tablename'
My question is, since they're using createSQLQuery, are they safe from injection. If they're not, could you give me an example to highlight the issue.
I tried using %08 (Backspace character) thinking I would be able to delete previous query characters for example (It didn't work ;) )
Thanks.
After some research it seems I won't be able to modify data with this security hole, however using ORDER BY (CASE WHEN ...) would allow to "scan" the tables and the data.
Is the column name specified using a parameterized statement or are you just concatenating text?
ex: in perl::DBI, the drivers support the following syntax:
$dbh->do("SELECt * FROM asdf ORDER BY ?", undef, $order_by);
The ? there is a form of parameterized statement which sanitizes the input automatically.
Related
Following this question, we also use another syntax in our SQL queries, which looks like that :
SELECT
id
FROM
myTable
WHERE
first_criteria = true
//withCondition//AND second_criteria = false
The //withCondition// syntax allows us to update the query by activating or disabling said line.
If possible, I'd want to be able to tell PhpStorm this specific syntax in SQL queries is a comment, so it won't display an error anymore.
I have some code that translates a user's search word into a MySQL query:
String sql = String.format("SELECT * FROM my_table WHERE my_column = '%s'", value);
HibernateUtil.getCurrentSession().createSQLQuery(sql);
To test if I was protected—and because I think it would be fun to learn in this way—I wanted to try to SQL inject my own application. So I searched for:
x'; DROP TABLE test;--
which results in the following query:
SELECT * FROM my_table WHERE my_column = 'x'; DROP TABLE test;--
But Hibernate throws a SQLGrammarException. When I run this code via phpMyAdmin, it correctly drops the test table.
How is Hibernate validating my SQL? Perhaps more importantly—is this protecting me against SQL injection or should I be using setParameter. If it's not protecting me, can I have an example of some SQL that will perform the injection. I think it would be fun to actually verify.
You are protected against execution of more than one statement because createSQLQuery allows exactly one statement. It is not Hibernate which protects you here, but your JDBC driver respectively your database - because it does not know how to handle the separator ; in the context of a single statement.
But you are still not safe against SQL injection. There are plenty of other possibilities to inject SQL in that case. Just one example:
Imagine you are searching for some user specific items in your query:
String sql = String.format(
"SELECT * FROM my_table WHERE userId = %s AND my_column = '%s'",
currentUserId, value);
The user can now enter:
x' OR '1' = '1
Which will lead to the SQL:
SELECT * FROM my_table WHERE userId = 1234 AND my_column = 'x' OR '1' = '1'
And because AND has higher precedence, he will see all items - even those of other users.
Even your provided example can be dangerous, for example
x' OR (SELECT userName FROM USER) = 'gwg
will let me know if your database contains a user that is called gwg (assuming that I know your database layout, which I could find out with similar queries).
According to Hibermate documentation, the method createSQLQuery "Create a new instance of Query for the given SQL string", so we can assume that Hibernate do the SQL checking for a single query on every call of this method.
Important: createSQLQuery is deprecated on Hibernate, please check out the link given above to see newers ways to execute SQL queries.
Now, speaking about how you could protect yourself from SQL injection, the best way to do it is using parameters on your query.
This question has the exactly example you are in need for, please check it out.
Hope this help you in your studies, good luck!
I have a CRON job which executes a SELECT statement to grab records. When the SELECT runs on my dev machine, it produces the following statement:
SELECT `users`.* FROM `users` WHERE `users`.`id` = 87 LIMIT 1
This is successful.
When the SELECT runs on my production (hosted) machine it produces the statement with double quotes:
SELECT "users".* FROM "users" WHERE "users”.”id” = 87 LIMIT 1
This is not successful and I get a MySQL 1064 error,
#1064 - 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 '.* FROM "users" WHERE "users
The code is the same on both machines, but my dev MySQL is version 5.5.33, whereas production is 5.1.67 (I don't have control over this to set/update it)
Is there a way to force single quotes or another preferred method to handle this situation?
Thanks for your time and assistance.
--EDIT--
Here are the main code snippets that are invoked via my CRON job:
/lib/tasks/reports.rake
namespace :report do
desc "Send Daily Report"
task :daily => :environment do
User.where(:report_daily => 1).find_each do |user|
ReportsMailer.send_report(user, 'daily').deliver
end
end
/app/mailers/reports_mailer.rb
def send_report(user, date_increment)
#user = user
#date_increment = date_increment
get_times(user)
mail :to => user.email, :subject=> "Report: #{#dates}"
end
--EDIT2--
So it looks like I need to use slanted single quotes (`) in order for this to work successfully. How do I force my app or MySQL to use these instead of double (") quotes?
I don't know why it does this, but I do know that if you're referencing column names in MYSQL, you need to use ``, whereas values / data should be wrapped in "", like this:
SELECT `users`.* FROM `users` WHERE `users`.`id` = "87" LIMIT 1
I learnt this the hard way back in the day when I was learning how to do simple MYSQL queries
Here's some documentation from MYSQL's site for you:
The identifier quote character is the backtick (“`”):
mysql> SELECT * FROM `select` WHERE `select`.id > 100;
Identifier quote characters can be included within an identifier if
you quote the identifier. If the character to be included within the
identifier is the same as that used to quote the identifier itself,
then you need to double the character. The following statement creates
a table named a`b that contains a column named c"d:
mysql> CREATE TABLE `a``b` (`c"d` INT);
Is there any reason you couldn't put some of your sql statement directly into your code like:
User.where("`report_daily`=1").find_each do |user|
After further inspection, and working with my hosting company, its turns out that my query is timing out on their server. Thanks to all that responded.
Since you are not using any literals, the format of the generated SQL statements should be determined by the underlying adapter. Perhaps you have a different mysql adapter installed or configured on each machine. Check the installed version. For example:
bundle show mysql
and also check the adapter configuration for your project in database.yml. For example:
adapter: mysql
A comparison of the results of these checks between each machine should tell you if you are using different adapters on the two machines.
I am trying to use the following bit of code to help in seeding my database. I need to add data continually over development and do not want to have to completely reseed data every time I add something new to the seeds.rb file. So I added the following function to insert the data if it doesn't already exist.
def AddSetting(group, name, value, desc)
Admin::Setting.create({group: group, name: name, value: value, description: desc}) unless Admin::Setting.find_by_sql("SELECT * FROM admin_settings WHERE group = '#{group}' AND name = '#{name}';").exists?
end
AddSetting('google', 'analytics_id', '', 'The ID of your Google Analytics account.')
AddSetting('general', 'page_title', '', '')
AddSetting('general', 'tag_line', '', '')
This function is included in the db/seeds.rb file. Is this the right way to do this?
However I am getting the following error when I try to run it through rake.
rake aborted!
Mysql2::Error: 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 'group = 'google' AND name = 'analytics_id'' at line 1: SELECT * FROM admin_settings WHERE group = 'google' AND name = 'analytics_id';
Tasks: TOP => db:seed
(See full trace by running task with --trace)
Process finished with exit code 1
What is confusing me is that I am generating correct SQL as far as I can tell. In fact my code generates the SQL and I pass that to the find_by_sql function for the model, Rails itself can't be changing the SQL, or is it?
SELECT * FROM admin_settings WHERE group = 'google' AND name = 'analytics_id';
I've written a lot of SQL over the years and I've looked through similar questions here. Maybe I've missed something, but I cannot see it.
"group" is a keyword so you can't use it as-is as an identifier, you have to quote it with backticks (for MySQL at least):
SELECT *
FROM admin_settings
WHERE `group` = 'google'
AND name = 'analytics_id'
Any SQL that Rails/ActiveRecord generates will use the quoted version of the column name so I'd guess that you're generating some SQL (or just a snippet of SQL for the WHERE clause) and neglecting to quote the column names.
I'd recommend against using group as a column name, use something else so that you don't have to worry about sprinkling backticks all over the place in your code.
group is an invalid field name if left unquoted, as it is a SQL keyword. To fix, surround it with backticks in your find_by_sql query, so your DB doesn't attempt to interpret it as the GROUP keyword.
I have a MySQL server as a linked server in Microsoft SQL Server 2008. For the link I use MySQL ODBC Connector version 5.1.8. When invoking queries using OPENQUERY (the only way I found of performing queries), problems occur. Simple queries, such as
SELECT * FROM OPENQUERY(MYSQL, 'SHOW TABLES')
work fine. Selection of individual columns, e.g.,
SELECT * FROM OPENQUERY(MYSQL, 'SELECT nr FROM letter')
works fine as well, but SELECT * syntax does not work. The query:
SELECT * FROM OPENQUERY(MYSQL, 'SELECT * FROM mytable')
raises an error:
Msg 7347, Level 16, State 1, Line 6
OLE DB provider 'MSDASQL' for linked
server 'MYSQL' returned data that does
not match expected data length for
column '[MSDASQL].let_nr'. The
(maximum) expected data length is 40,
while the returned data length is 0.
How can I make the SELECT * syntax work?
This problem happens if you are querying a MySQL linked server and the table you query has a datatype char(). This means fixed length and NOT varchar(). This happens when your fixed length field has a shorter string than the maximum length that SQL Server expected to get from the ODBC.
To fix this go to your MySQL server and change the datatype to varchar() leaving the length as it is. Example change char(10) to varchar(10).
Executing the following command before queries seems to help:
DBCC TRACEON(8765)
The error messages go away and queries seem to be working fine.
I'm not sure what it does though; I found it here: http://bugs.mysql.com/bug.php?id=46857
Strangely, SQL Server becomes unstable, stops responding to queries and finally crashes with scary-looking dumps in the logs a few minutes after several queries to the MySQL server. I am not sure if this has to do anything with the DBCC command, so I'm still interested in other possible solutions to this problem.
What I did to fix this since I can't modify the MySQL database structure is just create a view with a cast ex: CAST(call_history.calltype AS CHAR(8)) AS Calltype,
and select my view from MSSQL in my linked server.
The reason behind is that some strange types don't work well with the linked server (in my case the MySQL enum)
I found this
"The problem is that one of the fields
being returned is a blank or NULL CHAR
field. To resolve this in the Mysql
ODBC settings select the option "Pad
CHAR to Full Length"
Look at the last post here
An alternative would be to use the trim() function in your SELECT statement within OPENQUERY. The downside is you have to list each field individually, but what I did was create a view that calls OPENQUERY and then perfrom select * on the view.
Not ideal, but better than changing data types on tables!
Here is a crappy solution I came up with because I am unable to change the datatype to varchar as the db admin for the MySQL server is afraid it will cause issues with his scripts.
in my MySQL select query I run a case statement checking the character length of the string and add a filler character in front of the string "filling it up" to the max (in my case its a char(6)). then in the select statement of the openquery I strip the character back off.
Select replace(gradeid,'0','') as gradeid from openquery(LINKEDTOMYSQL, '
SELECT case when char_length(gradeid) = 0 then concat("000000", gradeID)
when char_length(gradeID) = 1 then concat("00000", gradeID)
when char_length(gradeID) = 2 then concat("0000", gradeID)
when char_length(gradeID) = 3 then concat("000", gradeID)
when char_length(gradeID) = 4 then concat("00", gradeID)
when char_length(gradeID) = 5 then concat("0", gradeID)
else gradeid end as gradeid
FROM sometableofmine')
it works but it probably is slower...
maybe you can make a MySQL function that will do the same logic, or come up with a more elegant solution.
I had the similar problem to this myself, which I resolved by wrapping the column-names in single ` style quotes.
Instead of...
column_name
...use...
`column_name`
Doing this helps the MySql query-engine should the column-name clash with a key or reserved-word.*
Instead of using SELECT * FROM TABLE_NAME, try to use all column names with quotes:
SELECT `column1`, `column2`, ... FROM TABLE_NAME
Example for normal datatype columns
SELECT * FROM OPENQUERY(MYSQL, 'SELECT `column1`, `column2`,...,`columnN` FROM mytable')
Example for ENUM datatype columns
SELECT * FROM OPENQUERY(MYSQL, 'SELECT `column1`, trim(`column2`) `column2`, `column3`,...,`columnN` FROM mytable')
*For those used to Sql Server, it is the MySql equivalent of wrapping a value in square-brackets, [ and ].