I have an application that uses EntityManager.createNativeQuery. Our quality gate(Sonar) is not so happy about it and shows me a critical error due to the risk of SQL-injection. Is there a "safe" way to create a dynamic sql-query in java?
My current code below:
return entityManager.createNativeQuery(stringBuilder.toString(), MyClass.class).getResultList();
To prevent SQL injection you could use parameter binding.
Example:
Query query = entityManager.createNativeQuery(
"SELECT title, author FROM books WHERE genre = ?1");
query.setParameter(1, "mystery");
You can find more examples here.
First, Sonar will complain even if you cleanse the input to your query string. You may have to // NOSONAR that line.
Sonar can't tell if you've scanned all the externally provided inputs to your query string. For example, a name input of "Joe ' Bloggs" will break your query string. I can't see how your are building your query; it's up to you to make it safe by validating any externally provided values.
Related
I am trying to pass a string value which is comma separated to birt report as parameter but failing
Java code
String userlist="\"a\",\"b\",\"c\"";
task.setParameterValue("userlist", userlist);
BeforeOpen has
params["userlist"].value.join("','");
SQL Query is
select * from users where name in (?)
I have already linked data set parameter to report parameter param_1
It's always giving me empty report even though DB table has 3 users. Any advise ?
Know your tools!
In this context: You have to understand SQL and the concept of bind variables, Javascript and BIRT.
Unfortunately every single piece of code you posted is wrong (or at least incomplete).
But you are on the right track: You can modify the SQL text in the beforeOpen event of the database. I'll sketch the idea here:
In your SQL, replace the ? with a placeholder like 'IN-LIST' (such that it is valid SQL).
You should still use the bind variable in the SQL (to avoid pitfalls caused by BIRT's caching mechanism), but in an effective no-op way, e.g. "where ? is not null".
In the beforeOpen event of the data set, you can modify the SQL text:
Get the original SQL text (var query = this.queryText; IIRC).
Split your report parameter into the individual search terms. How exactly to do this depends on your input format. In your example, you are using " around your search terms, which looks overly complicated, unless individual search terms may contain commas. You should now have a list of your search strings, e.g. ["a", "b", "c"].
Convert each term into a valid SQL string literal. Beware of SQL injection attacks, so carefully escape characters like single-quotes! You should now have a list of valid SQL string literals, e.g. ["'a"', "'b'", "'c'"].
Join your list of SQL string literals from 3) into a single string with ", ".
You should now have a string like 'a', 'b', 'c'.
In your query, replace your placeholder string with the string from 4).
Write the modified SQL text back to the DS object: this.queryText = query;
Probably there is also an example somewhere in the mists of the internet.
If you had invested five minutes more, you should have found an existing answer here on stack overflow: How to create a BIRT dataset that accepts multiple (CSV) values that it can be used inside "IN" clause in select statement, the only difference being that you are searching for a list of strings, while that question was about a list of numbers.
I know that PreparedStatements avoid/prevent SQL Injection. How does it do that? Will the final form query that is constructed using PreparedStatements be a string or otherwise?
Consider two ways of doing the same thing:
PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();
Or
PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();
If "user" came from user input and the user input was
Robert'); DROP TABLE students; --
Then in the first instance, you'd be hosed. In the second, you'd be safe and Little Bobby Tables would be registered for your school.
To understand how PreparedStatement prevents SQL Injection, we need to understand phases of SQL Query execution.
1. Compilation Phase.
2. Execution Phase.
Whenever SQL server engine receives a query, it has to pass through below phases,
Parsing and Normalization Phase:
In this phase, Query is checked for syntax and semantics. It checks whether references table and
columns used in query exist or not.
It also has many other tasks to do, but let's not go in detail.
Compilation Phase:
In this phase, keywords used in query like select, from, where etc are converted into format
understandable by machine.
This is the phase where query is interpreted and corresponding action to be taken is decided.
It also has many other tasks to do, but let's not go in detail.
Query Optimization Plan:
In this phase, Decision Tree is created for finding the ways in which query can be executed.
It finds out the number of ways in which query can be executed and the cost associated with each way
of executing Query.
It chooses the best plan for executing a query.
Cache:
Best plan selected in Query optimization plan is stored in cache, so that whenever next
time same query comes in, it doesn't have to pass through Phase 1, Phase 2 and Phase 3 again.
When next time query come in, it will be checked directly in Cache and picked up from there
to execute.
Execution Phase:
In this phase, supplied query gets executed and data is returned to user as ResultSet object.
Behaviour of PreparedStatement API on above steps
PreparedStatements are not complete SQL queries and contain placeholder(s),
which at run time are replaced by actual user-provided data.
Whenever any PreparedStatment containing placeholders is passed in to SQL Server engine,
It passes through below phases
Parsing and Normalization Phase
Compilation Phase
Query Optimization Plan
Cache (Compiled Query with placeholders are stored in Cache.)
UPDATE user set username=? and password=? WHERE id=?
Above query will get parsed, compiled with placeholders as special treatment, optimized and
get Cached.
Query at this stage is already compiled and converted in machine understandable format.
So we can say that Query stored in cache is Pre-Compiled and
only placeholders need to be replaced with user-provided data.
Now at run-time when user-provided data comes in, Pre-Compiled Query is picked up from Cache and placeholders are replaced with user-provided data.
(Remember, after place holders are replaced with user data, final query is not
compiled/interpreted again and SQL Server engine treats user data as pure data and not a
SQL that needs to be parsed or compiled again; that is the beauty of PreparedStatement.)
If the query doesn't have to go through compilation phase again, then whatever data replaced on the
placeholders are treated as pure data and has no meaning to SQL Server engine and it directly
executes the query.
Note: It is the compilation phase after parsing phase, that understands/interprets the query
structure and gives meaningful behavior to it. In case of PreparedStatement, query is
compiled only once and cached compiled query is picked up all the time to replace
user data and execute.
Due to one time compilation feature of PreparedStatement, it is free of SQL Injection
attack.
You can get detailed explanation with example here:
https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
The problem with SQL injection is, that a user input is used as part of the SQL statement. By using prepared statements you can force the user input to be handled as the content of a parameter (and not as a part of the SQL command).
But if you don't use the user input as a parameter for your prepared statement but instead build your SQL command by joining strings together, you are still vulnerable to SQL injections even when using prepared statements.
The SQL used in a PreparedStatement is precompiled on the driver. From that point on, the parameters are sent to the driver as literal values and not executable portions of SQL; thus no SQL can be injected using a parameter. Another beneficial side effect of PreparedStatements (precompilation + sending only parameters) is improved performance when running the statement multiple times even with different values for the parameters (assuming that the driver supports PreparedStatements) as the driver does not have to perform SQL parsing and compilation each time the parameters change.
I guess it will be a string. But the input parameters will be sent to the database & appropriate cast/conversions will be applied prior to creating an actual SQL statement.
To give you an example, it might try and see if the CAST/Conversion works.
If it works, it could create a final statement out of it.
SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))
Try an example with a SQL statement accepting a numeric parameter.
Now, try passing a string variable (with numeric content that is acceptable as numeric parameter). Does it raise any error?
Now, try passing a string variable (with content that is not acceptable as numeric parameter). See what happens?
Prepared statement is more secure. It will convert a parameter to the specified type.
For example stmt.setString(1, user); will convert the user parameter to a String.
Suppose that the parameter contains a SQL string containing an executable command: using a prepared statement will not allow that.
It adds metacharacter (a.k.a. auto conversion) to that.
This makes it is more safe.
SQL injection: when user has the chance to input something that could be part of the sql statement
For example:
String query = “INSERT INTO students VALUES(‘” + user + “‘)”
when user input “Robert’); DROP TABLE students; –” as the input, it causes SQL injection
How prepared statement prevents this?
String query = “INSERT INTO students VALUES(‘” + “:name” + “‘)”
parameters.addValue(“name”, user);
=> when user input again “Robert’); DROP TABLE students; –“, the input string is precompiled on the driver as literal values and I guess it may be casted like:
CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))
So at the end, the string will be literally inserted as the name to the table.
http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
PreparedStatement:
1) Precompilation and DB-side caching of the SQL statement leads to overall faster execution and the ability to reuse the same SQL statement in batches.
2) Automatic prevention of SQL injection attacks by builtin escaping of quotes and other special characters. Note that this requires that you use any of the PreparedStatement setXxx() methods to set the value.
PreparedStatement alone does not help you if you are still concatenating Strings.
For instance, one rogue attacker can still do the following:
call a sleep function so that all your database connections will be busy, therefore making your application unavailable
extracting sensitive data from the DB
bypassing the user authentication
Not only SQL, but even JPQL or HQL can be compromised if you are not using bind parameters.
Bottom line, you should never use string concatenation when building SQL statements. Use a dedicated API for that purpose, like JPA Criteria API.
In Prepared Statements the user is forced to enter data as parameters . If user enters some vulnerable statements like DROP TABLE or SELECT * FROM USERS then data won't be affected as these would be considered as parameters of the SQL statement
Is any other way to block sql injection in a textfield using regex?
This is what I use to block some special characters:
private static final String PATTERN =
"([^A-Za-z0-9.,'\"% _-]+)";
Just as I've said, SOME. I want to block ALL if possible and I only want to use regex. Please help. :)
The right way would be to use a PreparedStatement to execute your query. It will take care of possible injections.
You can find the docs here and the tutorial here
As for an example, after you set up a database connection, use something like this:
// add exception handling where necessary
PreparedStatement insertStmt = null;
String query = "INSERT INTO sometable"
+ "(name, value) VALUES"
+ "(?,?)"; //here the question marks stand for parameters
insertStmt = dbConnection.prepareStatement(query);
insertStmt.setString(1, "somename"); //set the first param
insertStmt.setInt(2, value); // set the second param
insertStmt.executeUpdate();
You will never be certain that you blocked all available SQL statements by using a regular expression. This is a very unstable and error prone approach. I would suggest using either a prepared statement or even better an ORM framework. You could check this tutorial regarding hibernate ORM and this tutorial for prepared statements.
These two mechanisms will protect you by definition from SQL injection as they are not based in using user input in their executed SQL, unless you specifically tell them so. Even then, they implement SQL injection protection which are far better than using a regular expression.
Hope I helped!
I am trying to validate data before inserting them into the database(POSTGRESQL). The data corresponding to email, zip code etc are easily validated with the use of Apache Commons Validator. But in case of names I used this:
^[a-zA-Z][ a-zA-Z]{1-30}$
This prevents any special characters from being added as name, but it fails to prevent users from adding DROP or GRANT as a name. As I am using PreparedStatement, I didn't think it was going to be a problem but it is now required that SQL keywords shouldn't go in the db as it may lead to a Second Order SQL Injection.
I thought of using blacklisting all SQL keywords (surely, this will prevent Huge Grant from logging into our site. :P) but it seems that there are >64 keywords. Is this (Blacklist filtering data for SQL Keywords) a proper approach for preventing Second Order SQL Injection? What are my options?
I am using this code:
String sql="INSERT INTO users (username, password, name) VALUES (?,?,?);";
try{
conn=SomeStaticClass.createConnection();
ps=conn.prepareStatement(sql);
ps.setString(1, dataBean.getUsername());
ps.setString(2, dataBean.getPassword());
ps.setString(3, dataBean.getName());
ps.execute();
}catch(SQLException e){
e.printStackTrace()
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(ps!=null){
ps.close();
}
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
Is this a proper approach for this kind of a situation?
No.
SQL injection happens when you assemble an SQL queries by concatenating Strings.
The "best practice" approach to preventing SQL injection is to use a PreparedStatement with constant SQL queries that have placeholders for the parameters. Then you use the prepared statement set methods to set values for each of the placeholder parameters. This approach will guarantee that any "nasty" string parameters containing SQL keywords will be interpreted as literal strings.
UPDATE - Using PreparedStatements consistently should protect against second order attacks too ... assuming that you are referring to something like this:
http://download.oracle.com/oll/tutorials/SQLInjection/html/lesson1/les01_tm_attacks2.htm
You just need to make sure that you don't build the SQL query string from anything that could possibly be tainted. Provided you handle any potentially tainted data using placeholders, it doesn't matter where it came from.
(Black listing SQL keywords will help to keep garbage out of your database. But as you mentioned, it can potentially cause damage to legitimate data and impact on your system's usability. I wouldn't do it. It would be better to rely on good programmer discipline ... and thorough code reviews.)
Second order injection only occurs if you store the keywords in the database and then later use them in an unsafe manner. If you use prepared statements and they are properly parameterized it won't occur. Cisco have a good summary of understanding SQL injection:
http://www.cisco.com/web/about/security/intelligence/sql_injection.html
Apart from your example of "Grant" there are also many such as IF, BY, IS, IN, TO that will occur very commonly in English language / names.
It is extreamly difficult, if not impossible, to ensure that all data in your database can be used with any script language (like SQL or HTML) without proper escaping in the future. It is impossible to distinguish between "safe" and "unsafe" characters before you now how the characters are going to be used anyway.
Trying to escape and clean all data before they are inserted into the database may lead you to belive that user-generated data in the database is "safe", which is a very dangerous belief. You can only know if the data is safe when you know how they are going to be used, and you will only know that when you actually use the data (since data in a database can live for a very long time).
The best strategy for avoiding this kind of problems is to always escape all data when you actually use them, either by using PreparedStatement like you do, properly escaping them when you use them in html, escaping them when you insert them into an email etc. etc.
I gave some examples in this answer:
How to allow specific characters with OWASP HTML Sanitizer?
Along with using PreparedStatement, you must check your input provided by user, on your webpages.
So now you have 2 different checks.
1. On your web pages, which will reduce processing time.
2. If something passes your initial check then preparedstatement will make sure your query is parsed properly.
E.g User is searching some item..
User input is
' OR ITEM in (Select ITEM from SOME_TABLE) OR ITEM = ''
And you are building your SQL, by concatenating the strings, then it will make SQL command as
Select * from TABLE_X WHERE ITEM = '' OR ITEM in (Select ITEM from SOME_TABLE) OR ITEM = ''
So your DATABASE is hacked, but in other case PreparedStatement it will parse your query and would not let make user to modify the SQL...
I'm fairly new to SQL and I'm currently reworking a java program that another
programmer has developed. When I print one of his query select statements the script contains sql syntax:
SELECT * from database WHERE id = ?
I just want know what =? is supposed to do? I've been googling around and I can't find any relevant answer.
It's not a SQL notation, but a JDBC (Java Database Connectivity) notation. The ? gets replaced with a parameter that is specified separately. Using this approach, instead of trying to substitute the parameter yourself into the string, helps prevent the risk of SQL injection.
The ? is a place holder, a parameter, so that you can pass it in dynamically and return different results for different parameters.
Somewhere in the code you should see that he adds the parameter to the Statement object and execute it.
Most likely you are using a tool that will replace the "?" with an actual value. I've seen this in other tools before such as SQL DTS (Data Transformation Services)... but that's showing how old I am :)
The ? is not part of the SQL language.
The ? is a place holder used in SQL queries when used with JDBC Prepared statement. Using a prepared statement has advantages over the normal statement specially when you use it repeatedly (say in a loop).
Here is an example :
PreparedStatement ps =
connection.prepareStatement("select name from users where user_name = ?");
ps.setString(1, "user1");
the "?" gets replace by "user1" when the query is run and the first name of the user with user name "user1" is returned.