Second Order SQL Injection - PreparedStatement - sql-server

The following section of my code is raising concern in "Second-Order SQL Injection".
private String function1 (String var1) {
String sql = "SELECT field1 FROM table1 WHERE field2 = ?";
PreparedStatement ps = null;
ResultSet resultSet = null;
String result = "";
try{
ps = conn.prepareStatement(sql);
ps.setString(1, var1);
resultSet = ps.executeQuery();
if(resultSet.next()){
result = rs.getString("fldDesc");
}
}catch(SQLException e){
e.printStackTrace();
}
}
However, as I know the preparedStatement should be safe against the 2nd Order SQL Injection, due to the separation of query and data.
Can I know why does it would raise a concern against it?

Related

Snowflake JDBC batch insert fails with "Bind variable ? not set" when specifying type in the query using "?::TIMESTAMP_NTZ"

We are using JDBC driver to connect to Snowflake and perform inserts.
Table can have multiple timestamp column, that means single insert query might have to deal with both timestamp_ntz and timestamp_ltz.
It works fine, when we specify the type in the query while binding values as below
INSERT INTO <TABLENAME> VALUES(?::TIMESTAMP_NTZ, ?::TIMETAMP_LTZ)
However, when we try the same with addBatch() and executeBatch(), it fails with "Bind variable ? not set"
Sample program -
try (Connection con = getConnection()) {
con.createStatement().execute("create or replace table Test_TSNTZ(t1 TIMESTAMPNTZ)");
try (PreparedStatement ps = con.prepareStatement("insert into Test_TSNTZ values (? ::TIMESTAMP_NTZ)")) {
java.sql.Timestamp t = java.sql.Timestamp.valueOf("2019-09-23 10:10:10.0");
for (int i = 0; i < 22; i++) {
ps.setTimestamp(1, t);
ps.addBatch();
}
ps.executeBatch();
try (Statement statement = con.createStatement()) {
try (ResultSet resultSet = statement.executeQuery("select * from Test_TSNTZ ")) {
while (resultSet.next()) {
System.out.println(resultSet.getTimestamp(1));
}
}
}
}
}
As I see, the JDBC connector (not sure if it's specific to Snowflake) is not able to bind the variables in a batch, if the variable is surrounded in a function or casting operator.
As a workaround, you can send the timestamp values as string. Snowflake will convert it to the column type:
try (Connection con = getConnection()) {
con.createStatement().execute("create or replace table Test_TSNTZ(t1 TIMESTAMP_NTZ, t2 TIMESTAMP_LTZ)");
try (PreparedStatement ps = con.prepareStatement("insert into Test_TSNTZ values ( ?, ? )")) {
java.sql.Timestamp t = java.sql.Timestamp.valueOf("2019-09-23 10:10:10.0");
for (int i = 0; i < 22; i++) {
ps.setString(1, t.toString());
ps.setString(2, t.toString());
ps.addBatch();
}
ps.executeBatch();
try (Statement statement = con.createStatement()) {
try (ResultSet resultSet = statement.executeQuery("select * from Test_TSNTZ ")) {
while (resultSet.next()) {
System.out.println(resultSet.getTimestamp(1) + "," + resultSet.getTimestamp(2) );
}
}
}
}
}

Call stored procedure specifying only parameters with a value

In an instance of SQL Server 2016 I have a stored procedure with dozens of parameters. For example:
CREATE PROCEDURE spName (
#par1 INT = NULL,
#par2 VARCHAR(10) = NULL,
....
....
#par98 INT = NULL,
#par99 INT = NULL,
) AS
BEGIN
....
....
END
I have a client written in C# that calls the stored procedure specifying only the parameters with a value. Ex:
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "spName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = dbConn;
cmd.Parameters.Add(new SqlParameter("par1", "val1"));
cmd.Parameters.Add(new SqlParameter("par47", "val47"));
...
cmd.ExecuteNonQuery();
It works perfectly! So, the procedure is executed and only the 2 parameters (par1 and par47) have a value. Other parameters maintain the default value (NULL).
I would do the same from a Java client using Microsoft JDBC driver 6.2.
I specify the parameters with List<Map<String, Object>>, so a list of couple parameterName-->parameterValue. The following method builds the PreparedStatement object:
private CallableStatement prepareStatement(String spName, Map<String, ?> parameters) throws SQLException {
setupConnection();
CallableStatement stmt = null;
try {
stmt = conn.prepareCall(getSpCallString(spName, parameters));
if (parameters != null) {
for (String parName : parameters.keySet())
stmt.setObject(parName, parameters.get(parName));
}
} catch (SQLException e) {
ApplicationLogging.severe("Cannot prepare callable statement", e);
throw e;
}
return stmt;
}
The method getSpCallString() generates a string of the type { call spName ?,?, ... , ? } with a number of ? as the number of parameters with a value passed to the procedure, so not all 99 parameters. If I have 2 parameter it generates the string { call spName ?,? }.
By passing for example par15=val15 and par47=val47 it raises the following exception:
com.microsoft.sqlserver.jdbc.SQLServerException: The index 2 is out of range.
I could resolve this putting in the call command the same number of ? as the number of parameter of the stored procedure but... I don't know the number of parameters for each stored procedure (and their position)!
In C# this is simply resolved because the parameters are assigned only with their name, so the number and the order of parameters can be really a black box.
Can I do this in some way in Java?
This is a confirmed deficiency in the current implementation of named parameter support for CallableStatement in the mssql-jdbc driver. Despite section 13.3.2 of the JDBC 4.2 specification stating ...
Named parameters can be used to specify only the values that have no default value.
... we seem to be required to provide a parameter placeholder for every possible parameter, and there doesn't appear to be a way to specify DEFAULT for the parameters we might otherwise simply omit.
As a workaround we could use code like this
public static ResultSet executeStoredProcedureQuery(
Connection conn, String spName, Map<String, Object> paramItems)
throws SQLException {
StringBuffer sqlBuf = new StringBuffer("EXEC ");
sqlBuf.append(spName);
int paramCount = 1;
for (String paramName : paramItems.keySet()) {
sqlBuf.append(
(paramCount++ > 1 ? ", " : " ") +
(paramName.startsWith("#") ? "" : "#") + paramName + "=?");
}
String sql = sqlBuf.toString();
myLogger.log(Level.INFO, sql);
// e.g., EXEC dbo.BreakfastSP #helpings=?, #person=?, #food=?
PreparedStatement ps = conn.prepareStatement(sql);
paramCount = 1;
for (String paramName : paramItems.keySet()) {
ps.setObject(paramCount++, paramItems.get(paramName));
}
return ps.executeQuery();
}
which we could call like this
// test data
Map<String, Object> paramItems = new HashMap<>();
paramItems.put("#person", "Gord");
paramItems.put("#food", "bacon");
paramItems.put("#helpings", 3);
//
ResultSet rs = executeStoredProcedureQuery(conn, "dbo.BreakfastSP", paramItems);
If using a third party library to facilitate calling such procedures is an option for you, then jOOQ certainly helps via its code generator for stored procedures, which generates stubs for each of your procedures, making such calls type safe. It includes support for:
Table valued functions
Table valued parameters
Defaulted parameters
In / Out parameters
Optional return value of procedures
Fetching undeclared update counts and result sets
Much more
In your case, you could write:
Spname sp = new Spname();
sp.setPar1("val1");
sp.setPar47("val47");
sp.execute(configuration); // The object containing your JDBC connection
sp.getResults(); // The result set(s) and update counts, if any
Behind the scenes, a JDBC CallableStatement is created, just like you would do manually:
try (CallableStatement s = c.prepareCall(
"{ ? = call [dbo].[spName] (#par1 = ?, #par47 = ?) }"
)) {
// Get the optional procedure return value that all procedures might return
s.registerOutParameter(1, Types.INTEGER);
s.setString(2, "val1");
s.setString(3, "val47");
s.execute();
// Lengthy procedure to fetch update counts and result set(s)
}
See this article if you want to generically fetch update counts and result set(s) with JDBC.
Disclaimer: I work for the company behind jOOQ.

Java PreparedStatement:com.microsoft.sqlserver.jdbc.SQLServerException:parameter index out of range

I'm try to execute a SQL query using java PreparedStatement in JAVA 8 and code blow
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
con = DriverManager.getConnection(URl,user,"");
String sql = "Select Grade from XS_KC where Snum=? and Cnum=?";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, "020101");
pst.setString(2, "102");
rs = pst.executeQuery();
while(rs.next())
{
System.out.print(rs.getString(1));
}
I get the fllowing error:com.microsoft.sqlserver.jdbc.SQLServerException:parameter index out of range
But if i use
stat = con.createStatement();
rs = stat.executeQuery("Select Grade from XS_KC where Snum='020101' and Cnum='101'");
I can get the result
What's wrong??
The first ? in your query is Unicode 0x1FFF rather than 0x3F00 (question mark). Try:
String sql = "Select Grade from XS_KC where Snum=? and Cnum=?";

Java & SQL Server exception: The statement did not return a result set

I'm creating an insurance management system for my DBMS project in university, and I am having a problem with deleting a record from SQL Server. It throws an exception:
SqlException: com.microsoft.sqlserver.jdbc.SQLServerException: The statement did not return a result set.
It also successfully deleted a record from my database. Could anyone please tell me how to remove this kind of exception?
String SQL="delete from INMS_3 where Agent_Id=? and First_Name=? and Last_Name=? and User_Name=? and Phone_no=?";
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
"databaseName=INMS;user=TestingUser;password=1234;";
Connection con = DriverManager.getConnection(connectionUrl);
System.out.println("Connected to sql server");
String str=jTextField1.getText();
String str1=jTextField2.getText();
String str2=jTextField3.getText();
String str3=jTextField4.getText();
String str4=jTextField5.getText();
PreparedStatement st = con.prepareStatement(SQL);
st.setString(1, str);
st.setString(2,str1);
st.setString(3,str2);
st.setString(4,str3);
st.setString(5, str4);
ResultSet rs = st.executeQuery();
if(rs.next());
{
JOptionPane.showMessageDialog(null,"Deleted Succesfully");
}
if(!rs.next())
{
JOptionPane.showMessageDialog(null,"Unable to delete");
}
else
{
JOptionPane.showMessageDialog(null,"Unable to delete");
}
} catch (SQLException e) {
System.out.println("SQL Exception: "+ e.toString());
} catch (ClassNotFoundException cE) {
System.out.println("Class Not Found Exception: "+ cE.toString());
}
I think you are using the wrong thing to perform the delete operation.
Try using st.executeUpdate() instead of ResultSet rs = st.executeQuery() - you are executing a delete rather than something that would return a result set.
This is not a problem with SQL Server. The problem is with your code (what is that? C#? The object is set to expect a result set from the server, but the query is a DELETE statement, and those return no rows... ever.
State the programing language, and research for how to execute statement instead requesting result sets.
This line makes sense for a SELECT not for an UPDATE
ResultSet rs = st.executeQuery();
if you're executing a delete statement, why are you executing
ResultSet rs = st.executeQuery();
Here's a sample c# ado.net. concept it the same if you're using java.
using(var conn = new SqlConnection("my connection string"))
{
var deleteCmd = new SqlCommand();
deleteCmd.Connection = conn;
deleteCmd.CommandType = CommandType.Text;
deleteCmd.CommandText = #"
DELETE Accounts
WHERE account_id = #p_account_id
";
deleteCmd.Parameters.AddWithValue("p_account_id", 123);
conn.Open();
deleteCmd.ExecuteNonQuery();
}

JDBC - prepareStatement - How should I use it?

I saw this example somewhere:
rs = connection.prepareStatement("select * from table").executeQuery();
Could I use this format, if I want to execute a query like this "Select * from table where column = "hello" "?
The way in which I usual I use prepareStatement object is something like this:
String sql = "select * from adresa where column = ?";
PreparedStatement pre = con.prepareStatement(sql);
pre.setString(1, i);
rs = pre.executeQuery();
Later Edit:
I don't understand. Pascal Thivent wrote that I can use the short version with In parameters, but Liu tells me this is not possible. :) Anw, using Pascal's version, i receive this error: void cannot be dereferenced
Here's a partial example how to use this interface:
static final String USER = "root";
static final String PASS = "newpass";
Connection conn = DriverManager.getConnection(myUrl, USER, PASS);
// create a sql date object so we can use it in our INSERT statement
Calendar calendar = Calendar.getInstance();
java.sql.Date startDate = new java.sql.Date(calendar.getTime().getTime());
// the mysql insert statement
String query = " insert into students (ID, last_name, first_name, birthday, hometown)"
+ " values (?, ?, ?, ?, ?)";
// create the mysql insert preparedstatement
PreparedStatement preparedStmt = conn.prepareStatement(query);
preparedStmt.setInt(1, 808027);
preparedStmt.setString(2, "Davis");
preparedStmt.setString(3, "Felicita");
preparedStmt.setDate(4, startDate);
preparedStmt.setString(5, "Venice");
// execute the preparedstatement
preparedStmt.execute();
conn.close();
You can only use the first form if there are no bind variables (question marks) in the query. It's just a shortened version of what you posted.
Also, if you use the shortened form you won't have the opportunity to reuse the PreparedStatement object.
of course u can use a string variable for the query in which u put in ur dynamic data and run it.
rs = connection.prepareStatement(variable).executeQuery();
The long form is often, but prepared statements can be precompiled by the db, and if used properly will help prevent sql injection.
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
conn = getConn();
ps = conn.prepareStatement("select * from x where y = ? "); //note no sb.append()'s or +'s, to helps prevent sql injection
ps.setLong(1, 12l);
rs = ps.executeQuery();
while (rs.next()) {
... act ...
}
} catch ( Exception e) {
} finally {
if (rs != null) rs.close();
if (ps != null) ps.close();
if (conn != null) conn.close();
}
Who said java was verbose. :)

Resources