I am working on a SQL Developer-like application, in which user enters some SQL command and the result from the server is shown in a <div> on web page.
The problem is that user can enter ANY string, valid SQL or not, for example if an user sends select * from employees; I want to receive and display in
the <div> text EXACTLY as below :
+---------+----------+---------------+----------+
| user_id | username | full_name | password |
+---------+----------+---------------+----------+
| 1 | john | John Doe | admin |
And when he enters a bad SQL string, the <div> message should be the standard MySQL error strings , for example :
mysql> select * from usrsss;
ERROR 1146 (42S02): Table 'mydb.usrsss' doesn't exist
I know about security risk , I do not care about it at this point.
Can this be done, as I have no control on the SQL string syntax being sent by user?
First of all, the prompt you see there mysql> represents the MySQL Shell. This is not SQL or JDBC but a command line interface provided by MySQL.
This MySQL Shell allows you to execute:
SQL statements.
A variety of other statements that are NOT part of SQL.
The JDBC API you want to use will allow you to run the first group of statements -- the SQL statements. Unfortunately, it won't allow you to run the second one, so you are out of luck for this one.
Also, for the first group the JDBC API will provide error codes and error messages that are not exactly the same ones you see when using the MySQL Shell.
Bottom line, you can simulate some of these commands, but it will not be the exact same experience that you probably expect.
However... and this is a big one, why do you want to do this in the first place? One of my developers asked me if he could do this, since it's not difficult to implement; this way we could easily run any SQL command from the web page. Great? Well... no. This is a HUGE SECURITY RISK. If anyone hacks your web page, you would be exposing the whole database.
In other words, don't deploy this code to production.
If you are using java.sql.Connection,
create a statement first by using .createStatement().
use .executeQuery() for searching
and .executeUpdate() for inserts, updates and deletes
When searching identify the number of columns to create a table.
ResultSet rs = statement.executeQuery(String sql);
ResultSetMetaData metaData = rs.getMetaData();
In ResultSetMetaData,
.getColumnCount() will give you the column count.
in a for loop create the columns, while creating .getColumnName(int index) will give you the column name.
After creating the table, iterate the ResultSet,
while (rs.next()) {
rs.getString(int columnIndex);
}
use the above method to get values, and add rows to your table.
Don't forget to surround the code block with
try{
} catch(Exception e){
e.getMessage();
}
so if anything goes wrong you can get the SQLException thrown. That will include a message, the probable cause for the error.
Work your way out... Enjoy!
Related
I am currently in the process of coding a script, using LUA. The script itself is designed to check if certain jobs are whitelisted (its a FiveM script). When switching jobs, it will save your current rank into the database, then switch over to the new job, so if you ever want to go back to the old one you will have your rank restored.
MySQL.Sync.execute('UPDATE users SET `#jobselect` = #currentrank WHERE identifier
= #identifier3', {
['#jobselect'] = currentjob2,
['#currentrank'] = xPlayer.job.grade,
['#identifier3'] = xPlayer.getIdentifier()
})
Through the console, I ran each of those variables, currentjob2, xPlayer.job.grade, etc. They all return what I expected them to. Then when I try to execute that MySQL query, I get the following error:
Unknown Column ''ems_rank'' in 'field list'
ems_rank is the result of the variable currentjob2 (just letting you know)
So, I can tell the issue is that the column's name is just ems_rank whereas the code is reading the quotation marks and assuming the column is called 'ems_rank' when it isnt. So if anybody could help me find a way to remove the quotations, that would be much appreciated.
Bare in mind I am coding all this in LUA.
So I have a server set up to serve payment requests. A user enters their credit card details in a form.
Query to inject here:
$sql = "UPDATE users SET credit_card'".$credit_card."', cvv='".$cvv."', expdate='".$exp."' WHERE userid='".$_SESSION['userid']."'";
I am trying to change another users password from this query.
Where the $credit_card is posted from a form. Im trying to inject the $credit_card part by writing my own query and getting rid of the rest by adding ;-- to the end.
The statement I am using for $credit_card is :
', password='test' where userid='10';--
Now, I am positive this was working yesterday but now the following error appears and I cannot wrap my head around it. Any help please?
Query failed: UPDATE users SET credit_card'', password='test' WHERE userid='20';--, cvv='', expdate='' WHERE userid='20'
Not all database functions accept multiple statements so the ; delimiter may be considered unexpected input.
The syntax for single-line comments in MySQL is -- Foo (please note the white space after the double-dash).
If the server code is yours, you can just print the actually error message generated by the server (and not some generic "something went wrong" text). If it isn't, just copy and paste the SQL code from the error message into your favourite MySQL client.
So, I am trying to execute a query using ArcGIS API, but it should match any Json queries. I am kind of new to this query format, so I am pretty sure I must be missing something, but I can't figure out what it is.
This page allows for testing queries on the database before I actually implement them in my code. Features in this database have several fields, including OBJECTID and Identificatie. I would like to, for example, select the feature where Identificatie = 1. If I enter this in the Where field though (Identificatie = 1) an error Failed to execute appears. This happens for every field, except for OBJECTID. Querying where OBJECTID = 1 returns the correct results. I am obviously doing something wrong, but I don't get it why OBJECTID does work here. A brief explanation (or a link to a page documenting queries for JSON, which I haven't found), would be appreciated!
Identificatie, along with most other fields in the service you're using, is a string field. Therefore, you need to use single quotes in your WHERE clause:
Identificatie = '1'
Or to get one that actually exists:
Identificatie = '1714100000729432'
OBJECTID = 1 works without quotes because it's a numeric field.
Here's a link to the correct query. And here's a link to the query with all output fields included.
This should be the simplest thing but for some reason it's eluding me completely.
I have a Sequel connection to a database named DB. It's using the Mysql2 engine if that's important.
I'm trying to update a single record in a table in the database. The short loop I'm using looks like this:
dataset = DB["SELECT post_id, message FROM xf_post WHERE message LIKE '%#{match}%'"]
dataset.each do |row|
new_message = process_message(row[:message])
# HERE IS WHERE I WANT TO UPDATE THE ROW IN THE DATABASE!
end
I've tried:
dataset.where('post_id = ?', row[:post_id]).update(message: new_message)
Which is what the Sequel cheat sheet recommends.
And:
DB["UPDATE xf_post SET message = ? WHERE post_id = ?", new_message, row[:post_id]]
Which should be raw SQL executed by the Sequel connector. Neither throws an error or outputs any error message (I'm using a logger with the Sequel connection). But both calls fail to update the records in the database. The data is unchanged when I query the database after running the code.
How can I make the update call function properly here?
Your problem is you are using a raw SQL dataset, so the where call isn't going to change the SQL, and update is just going to execute the raw SQL. Here's what you want to do:
dataset = DB[:xf_post].select(:post_id, :message).
where(Sequel.like(:message, "%#{match}%"))
That will make the where/update combination work.
Note that your original code has a trivial SQL injection vulnerability if match depends on user input, which this new code avoids. You may want to consider using Dataset#escape_like if you want to escape metacharacters inside match, otherwise if match depends on user input, it's possible for users to use very complex matching syntax that the database may execute slowly or not handle properly.
Note that the reason that
DB["UPDATE xf_post SET message = ? WHERE post_id = ?", new_message, row[:post_id]]
doesn't work is because it only creates a dataset, it doesn't execute it. You can actually call update on that dataset to run the query and return number of affected rows.
Here's the story. I'm testing doing some security testing (using zaproxy) of a Laravel (PHP framework) application running with a MySQL database as the primary store for data.
Zaproxy is reporting a possible SQL injection for a POST request URL with the following payload:
id[]=3-2&enabled[]=on
Basically, it's an AJAX request to turn on/turn off a particular feature in a list. Zaproxy is fuzzing the request: where the id value is 3-2, there should be an integer - the id of the item to update.
The problem is that this request is working. It should fail, but the code is actually updating the item where id = 3.
I'm doing things the way I'm supposed to: the model is retrieved using Eloquent's Model::find($id) method, passing in the id value from the request (which, after a bit of investigation, was determined to be the string "3-2"). AFAIK, the Eloquent library should be executing the query by binding the ID value to a parameter.
I tried executing the query using Laravel's DB class with the following code:
$result = DB::select("SELECT * FROM table WHERE id=?;", array("3-2"));
and got the row for id = 3.
Then I tried executing the following query against my MySQL database:
SELECT * FROM table WHERE id='3-2';
and it did retrieve the row where id = 3. I also tried it with another value: "3abc". It looks like any value prefixed with a number will retrieve a row.
So ultimately, this appears to be a problem with MySQL. As far as I'm concerned, if I ask for a row where id = '3-2' and there is no row with that exact ID value, then I want it to return an empty set of results.
I have two questions:
Is there a way to change this behaviour? It appears to be at the level of the database server, so is there anything in the database server configuration to prevent this kind of thing?
This looks like a serious security issue to me. Zaproxy is able to inject some arbitrary value and make changes to my database. Admittedly, this is a fairly minor issue for my application, and the (probably) only values that would work will be values prefixed with a number, but still...
SELECT * FROM table WHERE id= ? AND ? REGEXP "^[0-9]$";
This will be faster than what I suggested in the comments above.
Edit: Ah, I see you can't change the query. Then it is confirmed, you must sanitize the inputs in code. Another very poor and dirty option, if you are in an odd situation where you can't change query but can change database, is to change the id field to [VAR]CHAR.
I believe this is due to MySQL automatically converting your strings into numbers when comparing against a numeric data type.
https://dev.mysql.com/doc/refman/5.1/en/type-conversion.html
mysql> SELECT 1 > '6x';
-> 0
mysql> SELECT 7 > '6x';
-> 1
mysql> SELECT 0 > 'x6';
-> 0
mysql> SELECT 0 = 'x6';
-> 1
You want to really just put armor around MySQL to prevent such a string from being compared. Maybe switch to a different SQL server.
Without re-writing a bunch of code then in all honesty the correct answer is
This is a non-issue
Zaproxy even states that it's possibly a SQL injection attack, meaning that it does not know! It never said "umm yeah we deleted tables by passing x-y-and-z to your query"
// if this is legal and returns results
$result = DB::select("SELECT * FROM table WHERE id=?;", array("3"));
// then why is it an issue for this
$result = DB::select("SELECT * FROM table WHERE id=?;", array("3-2"));
// to be interpreted as
$result = DB::select("SELECT * FROM table WHERE id=?;", array("3"));
You are parameterizing your queries so Zaproxy is off it's rocker.
Here's what I wound up doing:
First, I suspect that my expectations were a little unreasonable. I was expecting that if I used parameterized queries, I wouldn't need to sanitize my inputs. This is clearly not the case. While parameterized queries eliminate some of the most pernicious SQL injection attacks, this example shows that there is still a need to examine your inputs and make sure you're getting the right stuff from the user.
So, with that said... I decided to write some code to make checking ID values easier. I added the following trait to my application:
trait IDValidationTrait
{
/**
* Check the ID value to see if it's valid
*
* This is an abstract function because it will be defined differently
* for different models. Some models have IDs which are strings,
* others have integer IDs
*/
abstract public static function isValidID($id);
/**
* Check the ID value & fail (throw an exception) if it is not valid
*/
public static function validIDOrFail($id)
{
...
}
/**
* Find a model only if the ID matches EXACTLY
*/
public static function findExactID($id)
{
...
}
/**
* Find a model only if the ID matches EXACTLY or throw an exception
*/
public static function findExactIDOrFail($id)
{
...
}
}
Thus, whenever I would normally use the find() method on my model class to retrieve a model, instead I use either findExactID() or findExactIDOrFail(), depending on how I want to handle the error.
Thank you to everyone who commented - you helped me to focus my thinking and to understand better what was going on.