Vulnerable to SQL Injection? - java

I'm currently creating a Java application which uses MySQL.
I have read that in certain situations you should use a prepared statement to prevent SQL injection.
By now I don't use prepared statements and I'm actually a bit worried that my Code might be vulnerable to SQL injection.
Situations where MySQL queries are executed:
Direct query from inside the application(all variables used are defined in the application)
API call (another application makes a MySQL query --> All variables used are defined inside the application which makes the call)
MySQL code:
public static void Update(final String qry) {
try {
Statement stnt = connection.createStatement();
stnt.executeUpdate(qry);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static ResultSet Query(String qry) {
final ResultSet rs;
try {
Statement stnt = connection.createStatement();
rs = stnt.executeQuery(qry);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}

Depending on other factors, your application may remain vulnerable to SQL injection attack from someone with access to your application's environment.
For example, if the variables defined inside your code obtain their values from a configuration file, and later become part of SQL query, an attacker with access to your configuration files can execute a SQL injection attack by altering the content of your configuration file. Same goes for the other application: if there is a way to alter the content of the variables that go into the construction of your SQL query, it is likely possible to execute a successful SQL injection attack.
Using prepared statements provides catch-all defense against injection. The added complexity is well worth the trouble, though, because you plug a huge security hole with a relatively straightforward fix.

Can you post an example of String qry?
In general, it could be vulnerable if you execute a query with values defined not inside the program but defined from outside.
For example:
If you have a String name = null; and name is user-defined and you push in the database that name, this could be potentially a vulnerability.
You could avoid injection with preparedStatement because you set parameters with methods that process the value to strings.
Example:
try (Connection con = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD)){
try (PreparedStatement pst = con.prepareStatement(
"INSERT INTO " + TABLE_NAME
+ "(id, date, ip, name) "
+ "VALUES (?,?,?,?)")) {
pst.clearParameters();
pst.setInt(1, objectID);
pst.setDate(2, (Date) objectDate);
pst.setString(3, objectIP.getHostAddress());
pst.setString(4, object.getName());
int n = pst.executeUpdate();
System.out.println("Inserted " + n + " rows.");
} catch (SQLException e) {
System.out.println("Error in insertion: " + e.getMessage());
}
} catch (SQLException e){
System.out.println("Problem with connection to db:" + e.getMessage());
}
With TABLE_NAME as a static string variable.
An SQL Injection attempted on the name, for example, would pass through pst.setString, so the DB will not see that as a query but simply as a value to insert in the database.

Not sure which is your question, but yes, you might be vulnerable to sql injection, since you have scenarios in which query params are coming from outside your application (as you explain).
You might not be vulnerable anyway, if you are already doing checks when building your query string (or before) to prevent this scenario, even if you are not using prepared statements.
But i would strongly suggest to use prepared statements with bind variables

Related

H2 Database result set is readonly

I'm getting the SQLNonTransientException error when trying to update one of my rows in a H2 database.
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
//read data from database
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM BCSTASKS_SERVICE");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
if(rs.getString("Status").equals("Neu") && rs.getBoolean("wasShown") == false) {
rs.updateBoolean("WASSHOWN", true);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
The error message already suggests that I should use conn.createStatement and set the ResultSet to CONCUR_UPDATABLE. The error occurs at the line with rs.updateBoolean(...);
Error Message:
The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). [90140-210]
The problem is I don't know where and how I should use this method. In the same function or at the start of the program?
Most DB code I see doesn't attempt to use the fact that resultsets are updatable, and will instead fire off an additional UPDATE query, which works fine.
However, sure, H2 supports updateable resultsets too. However, some of the features that ResultSets have actually have quite a cost; the DB engine needs to do a boatload of additional bookkeeping to enable such features which have a performance cost. Lots of database queries are extremely performance sensitive, so by default you do not get the bookkeeping and therefore these features do not work. You need to enable them explicitly, that's what the error is telling you.
You're currently calling the 'wrong' preparedStatement method. You want the more extended one, where you pick and choose which additional bookkeeping you want H2 to do for you, in order to enable these things. You want this one.
conn.prepareStatement(
"SELECT * FROM BCSTASKS_SERVICE",
ResultSet.TYPE_SCROLL_INSENSITIVE, // [edited]
ResultSet.CONCUR_UPDATABLE);
That CONCUR_UPDATABLE thing is just a flag you pass to say: Please do the bookkeeping so that I can call .update.
[edited] This used to read 0 before, but as #MarkRotteveel pointed out, that's not valid according to the documentation.
You have to put update query for update data in database but you are going with select query that is the problem.
Select query is used if you have to fetch data from database.
Update query is used for update data in database where data already stored in database but you just overwrite data.
Here down is modified code:
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
PreparedStatement stmt = conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET wasShown = ? WHERE status = ? AND wasShown = ?");
stmt.setBoolean(1, true);
stmt.setString(2, "Neu");
stmt.setBoolean(3, false);
stmt.executeUpdate();
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
You need to create a separate query/prepareStatement for an update. In your case as far as I can see you need only one update query:
conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET WASSHOWN=true where
Status = 'Neu' and wasShown = false "

I am trying to use variable in select query with like clause but getting an error like invalid identifier

I am trying to use variable in select query with like clause but getting an error like invalid identifier. Here is my method...
private void searchBooks(){
try{
String SEARCHFORTHIS=Find_Book_Field.getText();
pst=conn.prepareStatement("SELECT * FROM BOOK WHERE NAME LIKE '%'+ SEARCHFORTHIS +'%'");
rs=pst.executeQuery();
Show_All_Books.setModel(DbUtils.resultSetToTableModel(rs));
}catch(SQLException e){
e.printStackTrace();
JOptionPane.showMessageDialog(null,e);
}
}
NEVER use string concatenation to build a SQL statement with user-supplied text values.
Well, unless you really want your code to be susceptible to SQL Injection attacks, allowing a hacker to steal your data and delete your tables.
You're already using PreparedStatement, so use it right.
You should also use try-with-resources for correct resource management.
private void searchBooks() {
String SEARCHFORTHIS = Find_Book_Field.getText();
String sql = "SELECT * FROM BOOK WHERE NAME LIKE ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%" + SEARCHFORTHIS + "%");
try (ResultSet rs = stmt.executeQuery()) {
Show_All_Books.setModel(DbUtils.resultSetToTableModel(rs));
}
} catch (SQLException e){
e.printStackTrace();
JOptionPane.showMessageDialog(null,e);
}
}
Try this
try{
String SEARCHFORTHIS=Find_Book_Field.getText();
pst=conn.prepareStatement("SELECT * FROM BOOK WHERE NAME LIKE '%"+ SEARCHFORTHIS +"%'");
rs=pst.executeQuery();
Show_All_Books.setModel(DbUtils.resultSetToTableModel(rs));
}catch(SQLException e){
e.printStackTrace();
JOptionPane.showMessageDialog(null,e);
}
}
Made changes in SELECT query only. you have not properly closed the double quote in it.
Note:
completely agree with Andreas, we should use Preparedstatment, in this kind of scenarios, to prevent SQL Injection attacks.

SQLite Query With Parameters Not Working in Java

I have a program that selects from a database given a table and column string.
public void selectAllFrom(String table, String column){
String sql = "SELECT ? FROM ?";
try (Connection conn = this.connect();
PreparedStatement pstmt = conn.prepareStatement(sql)){
pstmt.setString(1, column);
pstmt.setString(2, table);
ResultSet rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString(column));
}
} catch (SQLException e){
System.out.println(" select didn't work");
System.out.println(e.getMessage());
}
}
For some reason it is not working and it is going right to catch
Here is the connect() function as well:
private Connection connect(){
Connection conn = null;
// SQLite connection string
String url = "jdbc:sqlite:C:/sqlite/db/chinook.db";
try{
// creates connection to the database
conn = DriverManager.getConnection(url);
System.out.println("Connection to SQLite has been established");
} catch (SQLException e){
System.out.println(e.getMessage());
System.out.println("Connection didn't work");
}
return conn;
}
I know the problem is not with the database because I'm able to run other select queries without parameters. It is the parameters that are giving me the problem. Can anyone tell what the problem is?
A table or column name can't be used as a parameter to PreparedStatement. It must be hard coded.
String sql = "SELECT " + column + " FROM " + table;
You should reconsider the design so as to make these two constant and parameterize the column values.
? is a place holder to indicate a bind variable. When a SQL statement is executed, database first checks syntax, and validates the objects being referenced, columns and access permission for specified objects (i.e metadata about objects) and confirms that all are in place and valid. This stage is called parsing.
Post parsing, it substitutes bind variables to query and then proceeds for actual fetch of results.
Bind variables can be substituted in any place in query to replace an actual hard coded data/strings, but not the query constructs them selves. It means
You can not use bind variables for keywords of sql query (ex: SELECT, UPDATE etc.)
You can not use bind variables for objects or their attributes (i.e table names, column names, functions, procedures etc.)
You can use them only in place of a otherwise hard coded data.
ex: SELECT FIRST_NAME, LAST_NAME, 'N' IS_DELETED FROM USER_DATA WHERE COUNTRY ='CANADA' AND VERIFIED_USER='YES'
In above sample query, 'N','CANADA' and 'YES' are the only strings which can be replaced by a bind variable, not any other word.
Using bind variable is best practice of coding. It improves query performance (when used with large no. of queries in tuned database products like Oracle or MSSQL) and also protects your code against sql injection attacks.
Constructing query by concatenating strings (especially data part of query) is never recommended way. You can still construct a query by concatenation for other parts like table name or column name as long as those strings are not directly taken from input.
Below example is acceptable:
query = "Select transaction_id, transaction_date from ";
if (isHistorical(reportType)
{ query = query + "HISTORY_TRANSACTIONS" ;}
else
{query = query + "PRESENT_TRANSACTIONS" ; }
recommended practice is to use
String query_present = "SELECT transaction_id, transaction_date from PRESENT_TRANSACTIONS";
String query_historical = "SELECT transaction_id, transaction_date from HISTORY_TRANSACTIONS";
if (isHisotrical(reportType))
{
ps.executeQuery(query_historical);
}else{
ps.executeQuery(query_present);
}

Why is this query not returning any results?

I am leaning some Java for school, and for some reason the code below doesn't return any results? Is there a problem with this code? There is a single record in the mysql database, with a studentID (which is int(10)) of 12.
public static ResultSet GetByID(int studentID) {
// This method loads the mysql driver and establishes the database connection
Connect();
ResultSet results = null;
try {
String query = "SELECT * FROM student where studentID = ?";
PreparedStatement statement = Connection.prepareStatement(query);
statement.setInt(1, studentID);
results = statement.executeQuery();
} catch (SQLException ex) {
LogException(ex);
} catch (Exception ex) {
System.out.println(ex);
}
// This method terminates the mysql connection.
Disconnect();
return results;
}
The calling code is:
#Override
public ResultSet query() {
return DB.GetByID(getStudentID()); // this is 12
}
This does not return null, rather just an empty result set.
A ResultSet can only be used while the connection is still open. Read it before you "disconnect".
If fixing that doesn't give you a different result, the most likely cause is that the query didn't match any rows in the table.
You should get into the habit of closing connections, streams, etc in finally blocks to prevent resource leakage. (Your lecturer should have explained that. Check your notes / text book.)
Finally, since you are a beginner, it is worth pointing out that you should always conform to the accepted Java conventions for method naming. Java method names should start with a lower-case letter. GetByID should be getByID and Disconnect should be disconnect.
(And if your lecturer / tutor doesn't or didn't dock marks for that, he / she should be condemned to writing Visual Basic for the next 5 years for crimes against the Software Engineering industry.)
If you want to return the resultSet after closing the connection you have to use CachedRowSet.
As StephenC said, your ResultSet is empty after closing the connection.

Stored Procedures in Derby

I am new to Derby and to databases in general for that matter. How can I create a prepared statment for an embedded derby database? Im not sure because the database is embedded.
My String is:
final String updateString = "create table " + TABLE_NAME_TBL_IPS + " (" +
TABLE_COLUMN_COMPANY + " " + TABLE_COLUMN_COMPANY_DATA_TYPE+ "," +
TABLE_COLUMN_IP + " " + TABLE_COLUMN_IP_DATA_TYPE + ")";
Also what is the benefit of using this as a stored procedure instead of a prepared statement call?
It doesn't really matter if the database is embedded or not, as long as it has JDBC connectivity. In your case, Derby does provide you to the connection information.
Your code may look something like this:-
// much easier to read with String.format()... in my opinion
final String updateString = String.format("create table %s (%s %s, %s %s)",
TABLE_NAME_TBL_IPS,
TABLE_COLUMN_COMPANY,
TABLE_COLUMN_COMPANY_DATA_TYPE,
TABLE_COLUMN_IP,
TABLE_COLUMN_IP_DATA_TYPE);
Connection con = null;
try {
con = DriverManager.getConnection("jdbc:derby:yourDatabaseName");
PreparedStatement ps = con.prepareStatement(updateString);
ps.executeUpdate();
ps.close();
}
catch (SQLException e) {
e.printStackTrace();
}
finally {
try {
con.close();
}
catch (Exception ignored) {
}
}
Regarding your question whether to do so using a stored procedure or a PreparedStatement, there are bunch of information out there you are easily search. You generally use a stored procedure to group bunch of SQL statements whereas a PreparedStatement only allows you to execute one SQL statement. It is a good idea to use stored procedures if you intend to expose that API to allow your users to execute it regardless of technology (Java, .NET, PHP). However, if you are writing this SQL statement only for your Java application to work, then it makes sense to just use PreparedStatement.

Categories