I'm searching for a Java standard library to escape strings before using them in a SQL statement. This is mainly to prevent errors and to prevent SQL injection, too.
Unfortunately, I will not be able to use prepared statements as is is suggested in a lot of other threads.
Is there any standard library I can use?
I've seen StringEscapeUtils at Apache Commons but I really don't know if this is state of the art.
Thank you very much in advance!
This is a non-trivial problem and a critical one because of SQL injection security issues. I would instead consider using SQL ? type arguments instead of escaping. For example, to search for a particular string:
Instead of doing:
SELECT * FROM accounts
WHERE name = 'escapedstring1' AND password = 'escapedstring2'
I'd use:
SELECT * FROM accounts WHERE name = ? AND password = ?
You will then need to pass in the injected strings (without any escaping needed) as arguments to your SQL methods. Here's how to do it using JDBC. Not sure if that would apply to you.
Something like:
String statement = "SELECT * FROM accounts WHERE name = ? AND password = ?";
try {
PreparedStatement stmt = databaseConnection.prepareStatement(updateString);
// notice that it is 1 based, not 0 (ick)
stmt.setString(1, name);
stmt.setString(2, password);
ResultSet results = stmt.executeQuery();
Here's how ORMLite, my ORM library, does it as an example using a select argument.
Hope this helps.
You can use Apache Commons, it's the very mature project.
Related
I have the following prepared statement.
"select * from gfc.LSI_ELGBLTY where INSURANCE_ID = ? and SYS_CD = ? and ACCT_TYPE in (?)";
how can i append single quote before and after ?
for eg after passing params to the query, it should be like
"select * from gfc.LSI_ELGBLTY where INSURANCE_ID = '1234' and SYS_CD = 'AA' and ACCT_TYPE in 'SDF'";
You are not supposed to do it yourself, this is done either client-side by the JDBC driver or server-side by the database instance. Take a look at How does the MySQL JDBC driver handle prepared statements article to understand how it works.
Adding backslash would help.
\'?\'
I have a function
public void executeMyQuery(Connection con) {
PreparedStatement ps = con.prepareStatement("SELECT * FROM STUDENT WHERE ID = ?");
ps.setInt(1, 7);
ps.executeQuery();
}
if i will run this it will work fine. But I want to do like this.
if I will setInt it should include WHERE clause. (returns matched
row)
if I don't setInt it should exclude WHERE clause. (returns whole table)
Or is there is any way to dynamically remove or modify the string after WHERE Clause.
Disadvantages of using string based SQL for dynamic SQL
Other answers have shown how to achieve dynamic SQL using string based JDBC usage. There are many disadvantages to building SQL strings dynamically using string concatenation, including:
High risk of SQL injection if you accidentally concatenate user input to your SQL queries
Difficult to avoid syntax errors in non-trivial cases, when dynamic SQL gets more complex
Also, when you're using plain JDBC (which only supports indexed bind variables), rather than some utility such as Spring JdbcTemplate, MyBatis, jOOQ, etc. you will have to manually match ? placeholders with their corresponding indexes, which is another subtle source of errors.
Using a query builder
At some point, when you implement dynamic SQL queries more often, query builders will definitely help. The most popular ones are:
jOOQ (for dynamic SQL querying)
JPA Criteria Query (for dynamic JPAL querying)
There are many other options that are more or less well maintained. For very trivial cases (like the one in your question), you can also build your own simple predicate builder.
Disclaimer: I work for the company behind jOOQ.
You have to build your query dynamically, at the beginning of the method check whether id is null or equal 0. To make it easier you can use trick in where clause with 1=1 so where clause can be included all the time in the query.
public void executeMyQuery( Connection con, Integer id) {
String query = "SELECT *FROM STUDENT WHERE 1=1";
if(id != null){
query += "AND ID = ?";
}
PreparedStatement ps = con.prepareStatement(query);
if(id != null){
ps.setInt(1, id);
}
ps.executeQuery();}
ifNull/Coalesce work nicely for this, if you pass a null, it will select where the field equals itself.
SELECT *
FROM STUDENT
WHERE 1 = 1
and ID = ifNull(:ID, ID)
I'd also suggest something other than using ? for your variables, fine when you a couple but as you get a ton of them, difficult to keep track or modify. I've found https://github.com/dejlek/jlib/blob/master/src/main/java/com/areen/jlib/sql/NamedParameterStatement.java pretty easy, made a few modifications to fit my particular needs but SQL is much easier to read and doing substitutions in intellij db console are much easier when developing the SQL.
You can have two PreparedStatements defined in your program - one without the WHERE ID = ? clause, and another one with it.
Moreover, you are supposed to keep your PreparedStatements and re-use, so you better store them as a field, etc.
And then, when needing to get the data - call either the first prepared statement, or the second one.
Michael Dz is close to the solution in his answer, but there is a problem in the code : he calls setInt on a non existing preparedStatement.
Try something like this :
public void executeMyQuery( Connection con, int Id) {
StringBuffer sql = new StringBuffer();
sql.append("Select * FROM STUDENT");
if(Id > -1) {
sql.append(" Where ID = ?");
}
preparedStatement ps = con.prepareStatement(sql.toString());
if(ID > -1) {
ps.setInt(1, Id);
}
ps.executeQuery(); // You might want to catch the result of the query as well
}
Hope this helps !
I am using Appscan source edition for Java Secure Coding. It is reporting an SQL injection in my application. The issue is that we are generating the query dynamically in code so I cannot use a prepared statement. Instead I have to us e Esapi.encoder().encodeForSql(new OracleCodec(), query). AppScan does not consider this to mitigate the SQL injection issue.
final String s = "SELECT name FROM users WHERE id = " +
Esapi.encoder().encodeForSql(new OracleCodec(), userId);
statement = connection.prepareStatement(s);
This code additionally does not work for ESAPI.encoder()
How can I resolve this issue?
what you should do is
final String s = "SELECT name FROM users WHERE id = ?"
statement = connection.prepareStatement(s);
statement.setString(1, userId);
The documentation for encodeForSQL recommends a PreparedStatement which you can still use which dynamically generated queries:
Encode input for use in a SQL query, according to the selected codec
(appropriate codecs include the MySQLCodec and OracleCodec). This
method is not recommended. The use of the PreparedStatement interface
is the preferred approach. However, if for some reason this is
impossible, then this method is provided as a weaker alternative. The
best approach is to make sure any single-quotes are double-quoted.
Another possible approach is to use the {escape} syntax described in
the JDBC specification in section 1.5.6. However, this syntax does not
work with all drivers, and requires modification of all queries.
Let's check what the encoder is doing to see why your code have an injection vulnerability. The encoder calls encodeCharacter in the oracle codec which simply replaces single quotes with two single quotes:
public String encodeCharacter( char[] immune, Character c ) {
if ( c.charValue() == '\'' )
return "\'\'";
return ""+c;
}
This only makes sense if the value is inside single quotes, which string values would be. If id is actually an integer and you wanted to concatenate it with the query, then you would convert it to an int type first instead of using this encoder.
I have App scan through which i scan my projects but at statements like
preparedStatement = conn.prepareStatement(sql);
there is SQL.Injection Vulnerability , i am using esapi api for setting the value in prepared statement e.g.
preparedStatement.setString(1 , OracleEncoder.encode(code) );
OracleEncoder is doing This
ESAPI.encoder().encodeForSQL( ORACLE_CODEC,param);
Any idea how can i fix this Vulnerability?
Assuming you are doing static analysis, appscan does not have a mark up for your ESAPI API, you should create a SQLi validator mark up for your encodeForSQL method in appscan. That way the next time you scan, the scan engine will pick up the new mark up and understand the SQLi threat is neutralized by the esapi call.
You don't need to encode bind parameters to preparedStatement,
// preparedStatement.setString(1 , OracleEncoder.encode(code) );
preparedStatement.setString(1 , code );
The relevant part of the PreparedStatement.setString() JavaDoc says,
The driver converts this to an SQL VARCHAR or LONGVARCHAR value (depending on the argument's size relative to the driver's limits on VARCHAR values) when it sends it to the database.
If you are using ESAPI library function and prepared statement in your code,then you can mark this issues Not an issue.
Prepared statement is one of the mitigation technique to a avoid SQL injection .
preparedStatement = conn.prepareStatement(sql);
I'm trying to build a web page to better learn Java and SQL. My question is, is there a way in Java to make a generic SQL select statement? For example:
SELECT var1 FROM var2 WHERE var3=var4
or something of the sort.
My idea is to fill the vars with user selected items from the web page. I know this can be done in PHP using the Post method, but I'm not using PHP. Also, I've read about the Prepared Statement in Java, but seems only to work when the used after the comparison operator; ex:
SELECT * FROM table Where attr = ? &
Also, I do know i can do the hard coded version of "SELECT " + var1 + "FROM " + var2 + "WHERE attr = " + var3 + " " but that doesn't seem very generic and prone to a lot of errors.
Incase: I'm trying to build this test page using HTML & JSP.
What you are doing with the ? is parameterizing the query. The query can only be parameterized for values not names of tables or columns.
Every time you run a query. The database has to create a query plan. If you are running the same query again and again, you can reduce this overhead by creating a PreparedStatement.
The first execution of PreparedStatement will generate the query plan. The subsequent executions will reuse the same plan.
Same query here means, it is identical in all respects except values used in where clause, expressions etc.
If you change the Column or Table name or modify the structure of the query, then it is a different query and will require a different query plan. A PreparedStement is not useful in this case and you should stick to the hardcoded version you talked about. Because of this reason you will get an error if you try to parameterize Table or Column names in PreparedStement.
Having said that. It is not advisable to take such a generic approach for queries. If your queries are that simple, you can benefit from ORM tools. You would not have to maintain even a line of SQL. For complex queries you have an option of using ORM specific query language or JPQL or Native SQL. Look for JPA + Hibernate
Your specific usage is not permitted by JDBC. You need to hard code the table name when creating the prepared statement. If you really do want to do that I suggest you use String concatenation to create the SQL statements and then create a PreparedStatement with parameters to handle the where part. In case you are wondering why bother with PreparedStatements in the specific solution, it's to avoid SQL injection.
You can use PreparedStatement to achive your objective.
For example -
String query = "SELECT * FROM table Where attr = ?";
PreparedStatement pt = con.prepareStatement(query);
pt.setString(1, attribete);
pt.executeUpdate();
There is no such direct provision in any of SQL packaged classes or others to replace table, column names along with query parameter values, in a query string, using a single method.
You require to depend on both PreparedStatement and any of String methods replace(...) and replaceFirst(...) to achieve your requirement.
String sql = "Select $1, $2 from $3 where $4=? and $5=?";
sql = sql.replaceFirst( "$1", "col1_name" );
sql = sql.replaceFirst( "$2", "col2_name" );
sql = sql.replaceFirst( "$3", "table_name" );
sql = sql.replaceFirst( "$4", "col4_name" );
sql = sql.replaceFirst( "$5", "col5_name" );
// .. and so on
PreparedStatement pst = con.prepareStatement( sql );
// use relevant set methods to set the query parametrs.
pst.setXXX( 1, value_for_first_query_parameter ); // from a variable or literal
pst.setXXX( 2, value_for_second_query_parameter); // from a variable or literal
// ... and so on
If you are using JDBC, can try this
PreparedStatement statement = connection.prepareStatement("SELECT ? FROM ? WHERE ?=? ");
then
statement.setString(1, "column_name");
statement.setString(2, "table_name");
statement.setString(3, "column_name");
statement.setBigDecimal(4, 123);
If you are using other ORM like Hibernate or JPA, I believe there are also ways to do.