database.BeginTransaction(); String userId = dto.UserId; String firstQuery = "select * from users where userid='" + userId + "'"; ResultSet rs = database.executeQuery(firstQuery); // Use the first entry in the result set as a user with 10 columns rs.next(); User user = new(); user.Username = rs.getString(2); ... // Character-wise comparison of user's password with dto.Password, if incorrect, unauthorized is returned here
// Now the second query, which doesn’t need a return but must run successfully String secondQuery = "select * from settings where userid='" + userId + "'"; ResultSet rs = database.executeQuery(secondQuery); // Use the first entry in the result set as settings with 40 columns rs.next(); Settings settings = new(); settings.DarkMode = rs.getBoolean(2); ... database.CommitTransaction();
// catch block here
This code would look pretty bad in real life. I wanted to show my colleague that it's vulnerable to SQL injection, and I successfully exploited it using `UNION ALL` in the first query to retrieve a user with a self-specified password. However, it fails to compile for the second query due to a column count mismatch, and an error is thrown when the `UNION ALL` is executed.
I also tried to insert a user into the table by concatenating `SELECT` with `INSERT`, but since `CommitTransaction` is never called, it doesn't have any actual effect. So, while the code is SQL injectable, nothing serious can happen because the transaction is wrapped, right? This seems a bit absurd.