resultSet obtained if parameter containing whitespace concatenated, but not using setString - java

I have this piece of code, with a prepared statement. I know the query is redundant. the parameter id is a string <space>413530 (" 413530"). Please note the preceding whitespace character.
String query = "SELECT RSCode as id FROM Customer WHERE RSCode=?";
PreparedStatement newPrepStatement = connection
.prepareStatement(query);
newPrepStatement.setString(1, id);
resultSet1 = newPrepStatement.executeQuery();
while (resultSet1.next()) {
System.out.println("Got a result set.");
logindata.add(resultSet1.getString("id"));
}
I do not get any results after executing this query.
Now, if I use the same statements and append the parameter as part of the string as follows:
String query = "SELECT RSCode as id FROM Customer WHERE RSCode=" + id;
PreparedStatement newPrepStatement = connection
.prepareStatement(query);
resultSet1 = newPrepStatement.executeQuery();
while (resultSet1.next()) {
System.out.println("Got a result set.");
logindata.add(resultSet1.getString("id"));
}
I get a result as after executing this prepared statement. Same also works with a java.sql.statement
I wish to know why the driver ignores the whitespace in the second piece of code, but has a problem in the first part.

If you use setString the parameter will be bound as a string resulting in this SQL (considering the bound parameter an SQL string):
SELECT RSCode as id FROM Customer WHERE RSCode=' 0123';
If you use concatenation the SQL used will be (considering the concatenated value as an integer, since space will be ignored as part of the SQL syntax):
SELECT RSCode as id FROM Customer WHERE RSCode=<space>0123;
In this case I would advise to convert it to int or long or whatever it is and bind it with the right type. With setInt() or setLong().
And if you field is a string you could normalize it first using for example:
String normalizedValue = String.trim(value);
newPrepStatement.setString(1, normalizedValue);
or even direct in SQL like:
SELECT RSCode as id FROM Customer WHERE RSCode=TRIM(?);

In scenario - 1, the query will look like this
"SELECT RSCode as id FROM Customer WHERE RSCode=' 413530'"
In scenario - 2, the query will look like this
"SELECT RSCode as id FROM Customer WHERE RSCode= 413530"

Related

Return array of deleted elements postgresql JDBC

This is my database:
dragons
id, key, name, age, creation_date
users
id, name, user, pass
users_dragons
user_id, dragon_id
So this is my code for deleting dragons from the database that have a bigger key that the passed and belongs to a determination user. The SQL query works perfectly for deleting them but not for returning the array of keys from the deleted elements.
I tried using PreparedStatement but later I checked, as far as I know, that this class doesn't return arrays, and the CallableStatement is only for executing processes in the db, and I don't know how they return arrays.
String query = "" +
"DELETE FROM dragons " +
"WHERE id IN (SELECT d.id FROM dragons d, users u, users_dragons ud" +
" WHERE d.key > ?" +
" AND ud.dragon_id = d.iD" +
" AND ud.user_id in (select id from users where id = ?)) RETURNING key INTO ?";
CallableStatement callableStatement = connection.prepareCall(query);
int pointer = 0;
callableStatement.setInt(++pointer, key);
callableStatement.setInt(++pointer, credentials.id);
callableStatement.registerOutParameter(++pointer, Types.INTEGER);
callableStatement.executeUpdate();
return (int []) callableStatement.getArray(1).getArray();
The code is giving me the error, but is obvious because the CallableStatement needs a postgres function to run and not a simple SQL query
org.postgresql.util.PSQLException: This statement does not declare an OUT parameter.
Use { ?= call ... } to declare one.
at org.postgresql.jdbc.PgCallableStatement.registerOutParameter
.......
It would be really helpful how would be the correct JDBC algorithm to delete the elements from the database and return the array of keys of the deleted items.
You treat such a statement like a normal SELECT statement: use java.sql.PreparedStatement.executeQuery() or java.sql.Statement.executeQuery(String sql) to execute the statement and get a result set.
java.sql.CallableStatement is for calling Procedures (but you don't need it in PostgreSQL).

sql-injection finding when using a prepared-statement in java [duplicate]

I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721

SQL - Return row if Boolean is True

Im trying to create an SQL Statement that will differentiate between employees types. For example if the Boolean Type Manager Column is checked it will return. Im using the info to fill a Manager on Duty JCombo in Java.
Im trying
String sql = "SELECT Employees.Name FROM Employees WHERE Manager = 'true' ORDER BY Name ASC";
Cant seem to get it right.
In SQL the boolean field will be a bit so your SQL statement will need to be
String sql = "SELECT Employees.Name FROM Employees WHERE Manager = 1 ORDER BY Name ASC";
in SQL a boolean field is a bit field (0 or 1) so you have to check as:
String sql = "SELECT Employees.Name FROM Employees WHERE Manager = 1 ORDER BY Name ASC"

Invalid operation for read only resultset: updateString

Following is my code(Re-constructed) which select & update STATUS field depending upon the conditions. (Using Servlets, Oracle as Backend and JDBC driver)
ResultSet rs=null;
String query = "select A.NAME, A.ADDRESS, A.STATUS, B.CLASS from TABLE_ONE A, TABLE_TWO B where A.STATUS='N'";
pstmt = con.prepareStatement(query,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
rs = pstmt.executeQuery();
while(rs.next())
{
String name = rs.getString("NAME");
String address = rs.getString("ADDRESS");
String class = rs.getString("CLASS");
String msg = //Other statements to check what status to be set
if(msg.equals("OK"))
rs.updateString("STATUS", "S");
else
rs.updateString("STATUS", "E");
rs.updateRow();
}
I am getting the error while updating:
java.sql.SQLException: Invalid operation for read only resultset: updateString
Any suggestions will be appreciated.
Update 1:
The same code was working when select statement was selecting data from single table, so is there any issue when selecting data from two tables in single query?
[Note: As #javaBeginner has mentioned in comments it will work only for one table.]
The following limitations are placed on queries for enhanced result sets. Failure to follow these guidelines will result in the JDBC driver choosing an alternative result set type or concurrency type.
To produce an updatable result set (from specification):
A query can select from only a single table and cannot contain any join operations.
In addition, for inserts to be feasible, the query must select all non-nullable columns and all columns that do not have a default value.
* A query cannot use "SELECT * ". (But see the workaround below.)
* A query must select table columns only. It cannot select derived columns or aggregates such as the SUM or MAX of a set of columns.
To produce a scroll-sensitive result set:
A query cannot use "SELECT * ". (But see the workaround below.)
A query can select from only a single table.
Try This :
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
//Execute a query
String sql = "select A.NAME, A.ADDRESS, A.STATUS, B.CLASS from TABLE_ONE A, TABLE_TWO B where A.STATUS='N'";
ResultSet rs = stmt.executeQuery(sql);
//Extract data from result set
rs.beforeFirst();
while(rs.next())
{
String name = rs.getString("NAME");
String address = rs.getString("ADDRESS");
String class = rs.getString("CLASS");
String msg = //Other statements to check what status to be set
if(msg.equals("OK"))
rs.updateString("STATUS", "S");
else
rs.updateString("STATUS", "E");
rs.updateRow();
}
Just changed Prepared statement to create statement
SELECT * makes the resultSet readonly. SELECT COLUMN_NAME makes it updatable.
So instead of SELECT * FROM TABLE use SELECT COLUMN1, COLUMN2, ... FROM TABLE.

PreparedStatement not returning ordered ResultSet

I am having some problems and I'm sure it's something stupid.
So I have a query like
SELECT name, id, xyz FROM table ORDER BY ?
then later down the road setting the ? doing a
ps.setString(1, "xyz");
I am outputting the query and the value of xyz in the console. When I loop through the ResultSet returned from the PreparedStatement the values are not in the correct order. They are in the returned order as if I had left the ORDER BY clause off. When I copy/paste the query and the value into TOAD it runs and comes back correctly.
Any ideas to why the ResultSet is not coming back in the correct order?
The database will see the query as
SELECT name, id, xyz FROM table ORDER BY 'xyz'
That is to say, order by a constant expression (the string 'xyz' in this case). Any order will satisfy that.
? is for parameters, you can't use it to insert column names. The generated statements will look something like
SELECT name, id, xyz FROM table ORDER BY 'xyz'
so that your entries are sorted by the string 'xyz', not by the content of column xyz.
Why not run:
ps.setInteger(1, 3);
Regards.
EDIT: AFAIK Oracle 10g supports it.
PreparedStatement placeholders are not intend for tablenames nor columnnames. They are only intented for actual column values.
You can however use String#format() for this, that's also the way I often do. For example:
private static final String SQL_SELECT_ORDER = "SELECT name, id, xyz FROM table ORDER BY %s";
...
public List<Data> list(boolean ascending) {
String order = ascending ? "ASC" : "DESC";
String sql = String.format(SQL_SELECT_ORDER, order);
...
Another example:
private static final String SQL_SELECT_IN = "SELECT name, id, xyz FROM table WHERE id IN (%s)";
...
public List<Data> list(Set<Long> ids) {
String placeHolders = generatePlaceHolders(ids.size()); // Should return "?,?,?..."
String sql = String.format(SQL_SELECT_IN, placeHolders);
...
DAOUtil.setValues(preparedStatement, ids.toArray());
...
The database will see the query like this
SELECT name, id, xyz FROM table ORDER BY 'xyz'
I think you should add more variable like order_field and order_direction
I assume you have a method like below and I give you an example to solve it
pulbic List<Object> getAllTableWithOrder(String order_field, String order_direction) {
String sql = "select * from table order by ? ?";
//add connection here
PreparedStatement ps = (PreparedStatement) conn.prepareStatement(sql);
ps.setString(1,order_field);
ps.setString(2,order_direction);
logger.info(String.valueOf(ps)); //returns something like: com.mysql.jdbc.JDBC4PreparedStatement#a0ff86: select * from table order by 'id' 'desc'
String sqlb = String.valueOf(ps);
String sqlc = sqlb.replace("'"+order_field+"'", order_field);
String sqld = sqlc.replace("'"+order_direction+"'", order_direction);
String[] normQuery = sqld.split(":");
ResultSet result = conn.createStatement().executeQuery(normQuery[1]);
while(result.next()) {
//iteration
}
}

Categories

Resources