What is the safe way how to put table name as parameter into SQL query? You cannot put table name as parameter using PreparedStatement. Concatenating string to execute query with dynamic table name using Statement is possible, however it is not recommended because of risk of SQL injection. What is the best approach to do this?
The best way would be:
To put your table name between the characters used to delimit the name of the table which change from one database to another
And escape the provided table name accordingly such that SQL injection won't be possible anymore.
So for example in case of MySQL, the table name's delimiter is the backquote character and we escape it by simply doubling it.
If your query is SELECT foo from bar, you could rewrite your query as next:
String query = String.format("SELECT foo from `%s`", tableName.replace("`", "``"));
This way you inject the name of your table without taking the risk of seeing some malicious code being injected.
I would try to solve the design problem, so you don't have to set the table name dynamically. If this is not possible, I would go for a design where you manage a list of available tables and users pick one from there, BY ID, so you can retrieve the real table name from the chosen id and replace the table name placeholder with it, avoiding any chance of sql injection in the table name replacement.
There is a rationale behind allowing only actual parameters in dynamic JDBC queries: the parameters can come from the outside and could take any value, whereas the table and column names are static.
There can be use cases for parameterizing a table or a column name, mainly when different tables have almost same structure and due to the DRY principle you do not want to repeat several times the same query only changing the table (or column) name. But in that use case, the programmer has full control on the names that will substituted, and should carefully test that there is no typo in any of them => there is no possibility of SQL injection here, and it is safe to replace the table name in the query string.
That is quite different for a web application exposed on internet where a query will use what has been entered in a form field, because here anything could occur, including a semicolumn to terminate the original harmless query and forge a new harmfull one => SQL injection if you just concatenate strings instead of correctly building a parameterized query.
I cannot imagine a use case where the table name or a column name could be a string typed in a form field by a user, which would be the only reason to allow to parameterize them.
Related
I have a requirement to perform a scheduled dump of a SQL query from a web application. Initially it was an entire table (only the table name was configurable), but then the addition of a configurable WHERE clause was raised, along with a subset of columns.
The configurable options now required are:
columns
table name
where clause
At this point, it might as well just be the entire query, right?!
I know that SQLi can be mitigated somewhat by java.sql.PreparedStatement, but as far as I can tell, that relies on knowing the columns and datatypes at compile time.
The configurable items will not be exposed to end users. They will sit in a properties file within WEB-INF/classes, so the user's I am defending from here are sysadmins that are not as good as they think they are.
Am I being over cautious here?
If nothing else, can java.sql.PreparedStatement prevent multiple queries from being executed if, say, the WHERE clause was Robert'); DROP TABLE students;--?
A prepared statement will not handle this for you. With a prepared statement you can only safely add parameters to your query, not table names, column names or entire where clauses.
Especially the latter makes it virtually impossible to prevent injection if there are no constraints whatsoever. Column and table name parameters could be checked against a list of valid values either statically defined or dynamically based on you database structure. You could do some basic regex checking on the where parameter, but that will only really help against obvious SQL injection.
With the flexiblity you intend to offer in the form of SELECT FROM WHERE you could have queries like this:
SELECT mycolumn FROM mytable WHERE id = 1 AND 'username' in (SELECT username FROM users)
You could look at something like JOOQ to offer safe dynamic query building while still being able to constrain the things your users are allowed to query for.
Constraining your users in one way or another is key here. Not doing that means you have to worry not just about SQL injection, but also about performance issues for instance. Provide them with a visual (drag-and-drop) query builder for instance.
"It all depends".
If you have an application where users can type in the where clause as free text, then yes, they can construct SQL Injection attacks. They can also grind your server to a halt by selecting huge cartesian joins.
You could create a visual query builder - use the schema metadata to show a list of tables, and once the table is selected the columns, and for each column the valid comparisons. You can then construct the query as a parameterized query, and limit the human input to the comparison values, which you can in turn use as parameters.
It's a lot of work, though, and in most production systems of any scale, letting users run this kind of query is usually not particularly useful...
It's insecure to allow users to execute arbitrary queries. This is the kind of thing you'd see at Equifax. You don't want to allow it.
Prepared statements don't help make SQL expressions safe. Using parameters in prepared statements help make values safe. You can use a parameter only in the place where you would normally put a constant value, like a number, a quoted string, or a quoted date.
The easiest solution would be to NOT allow arbitrary queries or expressions on demand.
Instead, allow users to submit their custom query for review.
The query is reviewed by a human being, who may authorize the stored query to be run by the user (or other users). If you think you can develop some kind of automatic validator, be my guest, but IMHO that's bound to be a lot more work than just having a qualified database administrator review it.
Subsequently, the user is allowed to run the stored query on demand, but only by its id.
Here's another alternative idea: users who want to run custom queries can apply to get a replica of the database, to host on their own computer. They will get a dump of the subset of data they are authorized to view. Then if they run queries that trash the data, or melt their computer, that's their business.
In my aplication I use eclipselink and the criteria api.
My database have a table with a column called "order".
The problem is when I use the criteria api to create a select it made this sql:
SELECT id, order, name, phone, uri FROM campus
It throw a exception because "order" is a restrict keyword in sql
How can I force the criteria api to put quotes in the columns names?
The easiest (and IMHO the best) way is to change order to campus_order or something like that and avoid using SQL keywords as a field identifier. This practice typically causes problems.
I will be glad to know that criteria API has some kind of work around for this problem but I'd recommend you to rename the column. Today you are using criteria API, tomorrow you will use something else... But at the end of the day the good old SQL is generated and the last think you want is to find that one of your queries does not work because the column name equals to keyword of one of SQL versions.
Renaming the field is an easier option, but JPA will quote delimited fields. To mark it as delimited, just add quotes when defining the column: "\"order\"".
I am writing a DAO layer IN Java for my Tomcat server application,
I wish to use Prepared Statement wrapping my queries (1. parsing queries once, 2. defend against SQL injections),
My db design contains a MyISAM table per data source system. And most of the queries through DBO are selects using different table names as arguments.
Some of this tables may be created on the fly.
I already went though many posts that explain that i may not use table name as an argument for Prepared statement.
I have found solutions that suggest to use some type of function (e.g. mysql_real_escape_string) that may process this argument and append the result as a string to the query,
Is there any built in Jave library function that may do it in the best optimized way, or may be you may suggest to do something else in the DAO layer (i do not prefer to add any routines to the DB it self)?
Are you able to apply restrictions to the table names? That may well be easier than quoting. For example, if you could say that all table names had to match a regex of [0-9A-Za-z_]+ then I don't think you'd need any quoting. If you need spaces, you could probably get away with always using `table name` - but again, without worrying about "full" quoting.
Restricting what's available is often a lot simpler than handling all the possibilities :)
If you want to be extra safe than you can prepare a query and call it with supplied table name to check if it really exists:
PreparedStatement ps = conn.prepareStatement("SHOW TABLES WHERE tables = ?");
ps.setString(1, nameToCheck);
if(!ps.executeQuery().next())
throw new RuntimeException("Illegal table name: " + nameToCheck);
(The WHERE condition might need some correction because I don't have mysql under my fingers at the moment).
I have some trivial table in database (let say Oracle10g) and I need to implement at DAO ability to delete multiple records. The method remove() receives as a parameter an array of ids (integers).
For now I have a query string "DELETE FROM news WHERE id = ?" which I use at PreparedStatement. I simply add batch for every id from array and then perform execute on PreparedStatement.
I wonder if there any ability to perform it through one query statement, something like "DELETE FROM news WHERE id IN ?". But I cannot find how to properly set an array of integers instead of '?'.
The same question applies to Hibernate and JPA. If there any constructions to solve this ? Because now I use batch-like-way: add Query to Session on every id from array and commit transaction.
The best I've seen done is to dynamically build the String used by the PreparedStatement, inserting the proper # of ?, sequences, then use a for loop to call setInt as appropriate for each row - each row to be deleted in your case.
JPA provides a special syntax for this (can accept a Collection to populate the list of arguments), since it has to create the SQL anyway - and likely does so very similar to how I just described. Specifics as to the API calls (for both JPA and HQL) are available at Hibernate HQL Query : How to set a Collection as a named parameter of a Query? .
I am creating a method to update a row in a SQL Server 2008 database. The SQL String looks something like this:
private static final String UPDATE_ROW =
"UPDATE MyTable SET FieldOne = ?, FieldTwo = ? " +
"WHERE IDField = ?";
It's simplified, of course. But the rub is that not all of the fields will necessarily change. I know that with straight SQL you can just put the field name in for the value and nothing will change; however, I don't know how to do this with the Java PreparedStatement.
I could work around it by calling one update for each field to be changed (there are up to ten) for each row, but that is just fugly and I would really like to avoid it. Can anyone tell me how to put the field name in as a parameter value, or at least give me a clean solution?
I couldn't find a way to do what I described, so I ended up reading the values of the things I was updating and passing in those values.
It will be a lot more efficient if you do create specialized UPDATE statements that only state the columns that have changed.
If you always update all columns you'll generate a lot of overhead by e.g. updating indexed columns which will cause the corresponding index to be updated as well (and without the actual need for this).
This will happen even if you specify UPDATE foo SET bar = bar if I'm not mistaken. I don't think SQL Server optimizes such updates away.
Its good that you are trying to avoid generating a specialized statement for each update.
Are the fields not inter-related? Because if they are inter-related, the update had better maintain inter-field consistency. So you need to first read the values, and then write all of them back -- both the changed and unchanged ones.
If they really are completely unrelated to one another, have a series of updates all getting committed at the same time.
Usually, one ends up somewhere in-between -- there are clusters of fields that are inter-related. For example, a Person record that contains several fields related to BillingAddress. In such cases, have a prepared statement for each group of related fields.
If you are trying to avoid the cost of a read (to get the current values), then consider a stored procedure, where unchanged field values are encoded with NULLs.
If you are not using an ORM, you can also consider using a cursored ResultSet, and update the fields one by one, then commit the changes using updateRow(). See java.sql.ResultSet. To do the same thing as the Javadoc using a PreparedStatement, you will need to use a variant of Connection.prepareStatement(...).