SQL exception preparing query with ORMLite - java

I am using an ORM (ORMlite) and all my calls are going well until I get the following error.
Exception in thread "main" org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "
SELECT * FROM ""STORIES"" WHERE ""TITLE"" = 'Deepcut case leads 'NOT FOLLOWED[*]'' "; SQL statement:
SELECT * FROM Stories WHERE title = 'Deepcut case leads 'not followed'' [42000-152]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
at org.h2.message.DbException.get(DbException.java:167)
at org.h2.message.DbException.get(DbException.java:144)
at org.h2.message.DbException.getSyntaxError(DbException.java:179)
at org.h2.command.Parser.getSyntaxError(Parser.java:480)
at org.h2.command.Parser.prepareCommand(Parser.java:229)
at org.h2.engine.Session.prepareLocal(Session.java:426)
at org.h2.engine.Session.prepareCommand(Session.java:374)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1093)
at org.h2.jdbc.JdbcPreparedStatement.(JdbcPreparedStatement.java:71)
at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:601)
at com.j256.ormlite.jdbc.JdbcDatabaseConnection.compileStatement(JdbcDatabaseConnection.java:83)
at com.j256.ormlite.stmt.mapped.MappedPreparedStmt.compile(MappedPreparedStmt.java:44)
at com.j256.ormlite.stmt.StatementExecutor.buildIterator(StatementExecutor.java:169)
at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:119)
at com.j256.ormlite.dao.BaseDaoImpl.query(BaseDaoImpl.java:189)
I'm confused as to whats going wrong. I am calling the search from these lines:
// get our query builder from the DAO
QueryBuilder<Story, Integer> queryBuilder = StoryDao.queryBuilder();
// the 'title' field must be equal to title (a variable)
queryBuilder.where().eq(Story.TITLE_FIELD_NAME, title);
// prepare our sql statement
PreparedQuery<Story> preparedQuery = queryBuilder.prepare();
// query for all stories that have that title
List<Story> accountList = StoryDao.query(preparedQuery);

Syntax error in SQL statement " SELECT * FROM ""STORIES"" WHERE ""TITLE""...
#bemace is correct that there seem to be quotes in the title that is screwing up the escaping of strings generated by the query.
In ORMLite, you should use the SelectArg feature which will generate a query with SQL ? arguments and then pass the string to the prepared statement directly.
See the documentation on the SelectArg. With it, you'd do something like:
QueryBuilder<Story, Integer> queryBuilder = StoryDao.queryBuilder();
SelectArg titleArg = new SelectArg();
queryBuilder.where().eq(Story.TITLE_FIELD_NAME, titleArg);
PreparedQuery<Story> preparedQuery = queryBuilder.prepare();
titleArg.setValue(title);
List<Story> accountList = StoryDao.query(preparedQuery);

I'm kind of guessing but it looks like there's a problem with the value in the title field, maybe an unescaped quote mark?
I'm not familiar with ORMLite but title = 'Deepcut case leads 'not followed'' doesn't look right. Should probably be "Deepcut case leads 'not followed'" or 'Deepcut case leads \'not followed\'' or some such.

The correct syntax for the statement would be:
SELECT * FROM Stories WHERE title = 'Deepcut case leads ''not followed'' ';
Note the duplicated single quotes inside the string literal.
You will need to tell your ORM layer to follow the ANSI SQL rules for literals.

The exception says that there is some syntactical problem with your generated SELECT statement. Can you print out the generated query? Doing that might help you pin down the exact issue here.
EDIT: Looking closely at your trace shows that string escaping is not handled properly here. Is this your own QueryBuilder? Also, as per this link, are you using SelectArg or directly setting the title?

Related

How to prevent SQL injection when dealing with dynamic table/column names?

I am using jdbc PreparedStatement for data insertion.
Statement stmt = conn.prepareStatement(
"INESRT INTO" + tablename+ "("+columnString+") VALUES (?,?,?)");
tablename and columnString are something that is dynamically generated.
I've tried to parameterise tablename and columnString but they will just resolve to something like 'tablename' which will violate the syntax.
I've found somewhere online that suggest me to lookup the database to check for valid tablename/columnString, and cache it somewhere(a Hashset perhaps) for another query, but I'm looking for better performance/ quick hack that will solve the issue, perhaps a string validator/ regex that will do the trick.
Have anyone came across this issue and how do you solve it?
I am not a java-guy, so, only a theory.
You can either format dynamically added identifiers or white-list them.
Second option is way better. Because
most developers aren't familiar enough with identifiers to format them correctly. Say, to quote an identifier, which is offered in the first comment, won't make it protected at all.
there could be another attack vector, not entirely an injection, but similar: imagine there is a column in your table, an ordinary user isn't allowed to - say, called "admin". With dynamically built columnString using data coming from the client side, it's piece of cake to forge a privilege escalation.
Thus, to list all the possible (and allowed) variants in your code beforehand, and then to verify entered value against it, would be the best.
As of columnString - is consists of separate column names. Thus, to protect it, one have to verify each separate column name against a white list, and then assemble a final columnString from them.
Create a method that generates the sql string for you:
private static final String template = "insert into %s (%s) values (%s)";
private String buildStmt(String tblName, String ... colNames) {
StringJoiner colNamesJoiner = new StringJoiner(",");
StringJoiner paramsJoiner = new StringJoiner(",");
Arrays.stream(colNames).forEach(colName -> {
colNamesJoiner.add(colName);
paramsJoiner.add("?");
});
return String.format(template, tblName, colNamesJoiner.toString(), paramsJoiner.toString());
}
Then use it...
Statement stmt = conn.prepareStatement(buildStmt(tablename, [your column names]));
As an elaboration on #Anders' answer, don't use the input parameter as the name directly, but keep a properties file (or database table) that maps a set of allowed inputs to actual table names.
That way any invalid name will not lead to valid SQL (and can be caught before any SQL is generated) AND the actual names are never known outside the application, thus making it far harder to guess what would be valid SQL statements.
I think, the best approach is to get table and columns names from database or other non user input, and use parameters in prepared statement for the rest.
There are multiple solutions we can apply.
1) White List Input Validation
String tableName;
switch(PARAM):
case "Value1": tableName = "fooTable";
break;
case "Value2": tableName = "barTable";
break;
...
default : throw new InputValidationException("unexpected value provided for table name");
By doing this input validation on tableName, will allows only specified tables in the query, so it will prevents sql injection attack.
2) Bind your dynamic columnName(s) or tableName(s) with special characters as shown below
eg:
For Mysql : use back codes (`)
Select `columnName ` from `tableName `;
For MSSQL : Use double codes(" or [ ] )
select "columnName" from "tableName"; or
select [columnName] from [tableName];
Note: Before doing this you should sanitize your data with this special characters ( `, " , [ , ] )

What's wrong with my SQL update? Java [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
private void subtractCredit(String accountType){ //subtract credit by 1
String CREDITS = "UPDATE CUSTOMERS SET "+accountType+" = "+accountType+" -1, CREDITSUSED=CREDITSUSED+1 WHERE USERNAME='"+username+"'";
try{
ps=con.prepareStatement(CREDITS);
ps.executeUpdate();
}catch(Exception e){
System.out.println(e);
}
}
public String[] getAccount(String accountType){ //Generate a random account.
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
String[] arr = new String[2];
try{
ps = con.prepareStatement(accountType);
rs = ps.executeQuery();
if(rs.next()){
arr[0] = rs.getString("USERNAME");
arr[1] = rs.getString("PASSWORD");
subtractCredit(accountType);
}
}catch(Exception e){
System.out.println(e);
}
return arr;
}
Here is the catch exception.
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an
error in your SQL syntax; check the manual that corresponds to your
MariaDB server version for the right syntax to use near 'Select * FROM
TABLE ORDER BY RAND() = Select * FROM TABLE ORDER BY RAND() -1' at
line 1
PLEASE HELP!
What's wrong with my SQL update?
The problem is not in your SQL update!!
Look at the SQL in the error message:
Select * FROM TABLE ORDER BY RAND()
Questions you should ask yourself:
Does that look anything like the update SQL? Nope!!
And what is missing from the 'select'? There is no table name!
If getAccount() is the method that is responsible for this error, then the cause should be obvious to anyone who bothers to read the code. The value of accountType is wrong. If the code is excactly as you have shown us, accountType must contain the string "TABLE". That is not going to work because TABLE is an SQL reserved word. A table called TABLE is a schema design error, because it leads to SQL syntax errors.
The other possibility is that the code actually says this:
accountType = "Select * FROM TABLE " +
accountType + " ORDER BY RAND()";
If so, the problem is that you have called the method with an empty string as the account type.
When you have fixed that, I want you to focus on a number of other significant problems in your code:
Assembling SQL by string bashing like this:
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
is potentially dangerous. If the value of accountType can come from user input, an HTTP request parameter, or anything else that is not under your control, then this code is vulnerable to an SQL injection attack.
The normal solution is to use a constant SQL string with ? placeholders, and then inject the actual parameter values using PreparedStatement.setXxxx method calls. Unfortunately, a table name can't be injected that way.
Catching Exception like you are doing is a BAD IDEA. Sure, it catches the SQLException that you are anticipating. The problem is that it also catches a bunch of other exceptions that you may not be anticipating. For example, if the code in the try block had a bug that caused it to throw a NullPointerException ... you would catch that too.
Using System.out.println(e) to output a "diagnostic" is bad:
For an end user, the exception message is opaque an alarming.
For a developer, you really need a stacktrace.
Sending developer diagnostics to standard output is generally a bad idea. Use a logging framework.
Your error recovery is almost certainly wrong. If the SQL query fails, then your getAccount method returns an String[2] containing null strings. If the calling code doesn't test for this, then you are likely to get an NPE when you try to use the (bogus) account details.
The correct thing to do is most likely to throw another exception, or if you don't want to add code to handle this up-stack, then allow the SQLException to propagate by removing the try catch ... and declaring the exception in the method signature.
This is minor, but most people think that updating the value of a method parameter is bad style. You are doing this when you assign a new value to accountType. Better style would be to declare a local variable and use that to hold the SQL string.
Consider this sequence of statements (drawn from two methods, but the variable names correspond):
getAccount("TABLE"); // inferred
accountType = "Select * FROM "+accountType+" ORDER BY RAND()";
String CREDITS = "UPDATE CUSTOMERS SET "+accountType+" = "+accountType
+ " -1, CREDITSUSED=CREDITSUSED+1 WHERE USERNAME='"+username+"'";
ps=con.prepareStatement(CREDITS);
ps.executeUpdate();
The error indeed occurs in your UPDATE statement, but only part of it appears in the message. The sequence of statements above explains how it got that way. In particular, consider the effect of the assignment to accountType on the subsequent statements.

Spring JdbcTemplate with parameter returns empty List

I can't understand what wrong with it, i spent many hours trying to get my String object from method queryForList(..) and all for nothing. So here this stupid sql query:
String GET_EMAIL_BY_LDAP = "select ld.LDAP_EMAIL1 from IPS.ldap ld where ld.ldap_login = ?"
I have tried this also without '?' parameter and it is works perfectly.
Here is the way, how I use the method:
List<String> email = jdbcTemplate.queryForList(GET_EMAIL_BY_LDAP, String.class, userId.toUpperCase());
And this version of method also returns empty List:
List<String> email = jdbcTemplate.queryForList(GET_EMAIL_BY_LDAP, new Object[]{userId.toUpperCase()}, String.class);
P.S. getEmailById("DN270391RKA") - this is my user id parameter that passed. It hasn't spaces.
Type of ld.LDAP_EMAIL1 field in table is : NVARCHAR2(381 CHAR)
I have understood what it was!
ld.ldap_login values in my ( legacy :-D ) table have a tailing spaces. After minor editing of my sql statement it works. I just added TRIM() on this column.
"select ld.LDAP_EMAIL1 from IPS.ldap ld where TRIM(ld.ldap_login) = ?"
So, it seems if it is usual sql statement then when I execute query then oracle database does TRIM() automatically and in case of PreparedStatement doesn't.
List<String> email = jdbcTemplate.queryForList(GET_EMAIL_BY_LDAP, String.class, userId.toUpperCase());
This line is fully correct so it seems to be either a bad userID or your sql query is not correct.
Best way is to paste your sql query with an existing user id in your query browser and check if you get more than an empty result.
then use the jdbc template again and pass hardcoded previously working user id and check if you get the same results.
or: delete the userid clause and select all not specific to the user id.
Also make sure the ld.LDAP_EMAIL is String representation e.g varchar/text

Spring JdbcTemplate's queryForList() with many args is not readable; SQLQuery don't give me list with column names

Note: This may be a simple question but I can't find a short way to make it clear. So, sorry for this long question.
In a project I am responsible, I use Spring 2.5, Hibernate 3.3.2 as middleware and Oracle database. As database is related to many other projects, some queries as really very complicated and I can't get a solution with Hibernate's solutions (HQL, Criteria, etc...). So I feel more comfortable with JdbcTemplate's queryForX() methods, as an example;
String sql = "select * from myTable";
jdbc.queryForList(sql);
Sure there are mostly "where" conditions and params indeed:
jdbc.querForList(sql, new Object[]{obj1,obj2,obj3 /* and many more arguments... */})
In this case, I must write question marks "?" for my parameters, so my SQL query string turns out some messy and hard to read; something like this:
select t1.col1, t2.col2, t1.col, --...some cols ,
sum(nvl(some_col1,?)-nvl(other_col2,?)) over (partition by col1,col2,col3,col4) sum_of_cols
from weird_table t1, another_table t2
where t1.col20=? and sum_of_cols>? and t1.col3=t2.col3 --and many ?'s...
and not exists (
select ? from boring_table t3 where -- many ?'s
)
--group by and order by order by etc
So now, which question mark is for which parameter? It is obvious but hard to read. But there are some other solutions for binded params like:
select * from a_table t where t.col1= :col1 and t.col2= :col2 -- and many more ":param"s
For this type query, we can write if it were Hibernate:
Query q = hibernateTemplate.createQuery();
q.setString("col1","a value");
q.setInteger("col2", 3);
I think it is more readable and easy to understand which value is what. I know I can do this with SQLQuery;
SQLQuery sq = hibernateTemplate.createSQLQuery();
/* same as above setInteger() etc. */
But this sq.list() gives me a list without a column name. so I have a basic array which is difficult to use:
[[1,2,"a"],[1,2,"b"], ...]
But with queryForList() I get better one:
[{COL1=1,COL2=2,COL3="a"},{COL1=1,COL2=2,COL3="b"},...]
So if I use queryForList(), I must write a very messy params Object;
or I use SQLQuery and then I have to get my list without a map as column names.
Is there a simple solution with mapped list using more readable param setting (like query.setX()) ?
Well you can use NamedParameterJdbcTemplate to do just that
Heres a sample
String query = "INSERT INTO FORUMS (FORUM_ID, FORUM_NAME, FORUM_DESC)
VALUES (:forumId,:forumName,:forumDesc)";
Map namedParameters = new HashMap();
namedParameters.put("forumId", Integer.valueOf(forum.getForumId()));
namedParameters.put("forumName", forum.getForumName());
namedParameters.put("forumDesc", forum.getForumDesc());
namedParameterJdbcTemplate.update(query, namedParameters);
You check the complete example with the source code in the below link
Spring NamedParameterJdbcTemplate Tutorial

I have stuck with the simplest SQL query in java

I can't find the correct syntax of the following query in java,please help me.
String st = "SELECT COUNT('"+id+"') FROM '"+selected_table+"' ";
String st = "SELECT COUNT('"+id+"') FROM '"+selected_table+"'";
I think that the mistake is how to end the query...
Since I got the error Check the manual that corresponds to your MySQL server version for the right syntax to use near ''Customer'' at line 1
when I choose Customer table
You want to use backticks instead of single quotes around your object names.
String st = "SELECT COUNT(`"+id+"`) FROM `"+selected_table+"` ";
Table names should be surrounded by tick marks (`), not single quotes (')
String st = "SELECT COUNT('"+id+"') FROM `"+selected_table+"`";
^ use tick marks ^
What are the values of id and selected_table? What is the actual query string that is sent to the database?
Also, it's rarely a good idea to manually build a query like this using string concatenation. This makes it very easy for a bug to result in a gaping security hole, and it's a lot more difficult (and risky) to try to secure this approach than it is to just do it right.
Looks from your query that you are enclosing your id and selected_table in single quotes... For example, SELECT COUNT('ID') FROM 'CUSTOMER' which is wrong. should be in backtics `` or nothing...

Categories