Mysql Update does not works with variables - java

basically when i try to use update with variables in java it doesnt works
ex:
String update = "UPDATE Clientes "
+ "SET Nombre ='"+nombre+"' , Apellido='"+apellido+"', Telefono ='"+telefono+"' ,Calle='"+calle+"',NumeroPuerta='"+numpuerta+"' , Localidad ='"+localidad+"' "
+ "WHERE Cedula = '"+ci+"' ";
that is the one that doesnt work but if i try the same but without variables it works
ex:
String update = "UPDATE Clientes "
+ "SET Nombre ='luciano', Apellido='smith', Telefono ='111111', Calle ='avenue', NumeroPuerta = '456', Localidad='maldonado' "
+ "WHERE Cedula = '123123' ";
thank to every one responding

First of all: never do what you try now: SET Nombre ='" + nombre + "'...
It is direct path to SQL Injection attack.
Instead use prepared statement with parameters like SET Nombre=?.
Second: only to find what's wrong with your query just use debugger or print resulting update string and find how it is different from working/hardcoded version.
Still you must switch to Prepared Statement with parameters for real code
UPD: example to start with (it is pure JDBC)
assuming you already have an java.sql.Connection ready:
String nombre = "John";
String apellido = "Doe";
String cedula = "12345";
String updQuery = "UPDATE Clientes SET Nombre = ?, Apellido=? WHERE Cedula = ?";
PreparedStatement updStmt = myConnection.prepareStatement(updQuery);
updStmt.setString(1, nombre);
updStmt.setString(2, apellido);
updStmt.setString(3, cedula);
updStmt.execute();
Two things in JDBC to remember (forever :-) ):
there is no way to define same parameter multiple times in the SQL query.
Parameters are just ? marks and indexes go in order how ? appear from begin to the end of SQL query.
parameter indexes start with 1 not 0

First of all, this kind of Sql generation will lead you to Sql injection. You should use PreparedStatement with question mark and pass variables which Java will take care about sql injection, check, sql generation

Related

sql-injection finding when using a prepared-statement in java [duplicate]

I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721

Insert into database a scraped page containing double quotes with MySQL and Java

Imagine my page has a bunch of sections looking something like this (example page):
<div class="content">
</div>
My goal is to scrape the entire page into a MySQL DB entry. I currently do this like so:
//Declare SQL statement
String sql = "INSERT into rns " +
"(rns_pub_date, rns_headline, rns_link, rns_fulltext, constituent_id) values (\""+
rns.getRnsPubDate() + "\",\"" +
rns.getRnsHeadline() + "\",\"" +
rns.getRnsLink() + "\",\"" +
rns.getRnsFullText() + "\",\"" +
"(select constituent_id from constituent where constituent_name = " + rns.getRnsConstituentName() + "\")";
//SQL Statement Debug
Log.d(CLASS_NAME, "createRns. sqlStatement: " + sql);
//Initialize insertValues
insertValues = connect.prepareStatement(sql);
However, this falls over because there are multiple " marks in the page.
I can see a few options:
Escape the characters like this: '\"'
Replace the characters with: '"'
Remove all non-relevant data (the HTML) and save only the relevant data to the DB
I realise that there's also best practice with regards to preventing SQL injection. However this is a standalone system, so for the moment isn't an issue. Having said that if any answer can explain how to prevent that, I would prefer to implement that instead.
Edit 1:
Following on from #chrylis comment. This is what I have:
//Insert values into variables
String rns_pub_date = rns.getRnsPubDate();
String rns_headline = rns.getRnsHeadline();
String rns_link = rns.getRnsLink();
String rns_fulltext = rns.getRnsFullText();
String rns_constituent_name = rns.getRnsConstituentName();
//Prepare the SQL string
String sql = "INSERT into rns (rns_pub_date, rns_headline, rns_link, rns_fulltext,constituent_id) VALUES" + "(?,?,?,?,(select constituent_id from constituent where constituent_name = \"" + rns.getRnsConstituentName() + "\")";
//Prepare the statement
PreparedStatement prest = connect.prepareStatement(sql);
prest.setString(1, rns_pub_date);
prest.setString(2, rns_headline);
prest.setString(3, rns_link);
prest.setString(4, rns_fulltext);
prest.setString(5, rns_constituent_name);
However it provides this error:
Parameter index out of range (5 > number of parameters, which is 4).
Edit 2:
The insert was fixed by removing the escaped double quotes for the 5th parameter:
String sql = "INSERT into rns (rns_pub_date, rns_headline, rns_link, rns_fulltext, constituent_id) VALUES" + "(?,?,?,?,(select constituent_id from constituent where constituent_name = ?))";
Use PreparedStatement, there will be no need for escaping. Usage example is in API
It's not only a bad practice because of SQL injection, it's slow and inefficient, too, and has problems with quote characters. Use a parameterized query.

Passing Oracle functions as String in Java

I have a query like this..
String query = "UPDATE tbl_customer_policies SET "+
"start_date = ?," +
"next_pay_date = ?,"+
"maturity_date = ?, " +
"modified_at = CURRENT_TIMESTAMP,"+
"modifier = ?, status = ? " +
"WHERE id = ?";
Now in place of the place-holder for start_date I want to pass a string like SYSDATE.
What I am doing now is setting that string in a variable called String startDate = "SYSDATE" and binding it to that place-holder. But I guess it does not seem to work. I get this exception
ORA-01858: a non-numeric character was found where a numeric was expected
I have to pass Oracle functions like that. How to achieve that?
If it will always be sysdate you don't need to parameterize it.
Set it directly in the query like this:
String query = "UPDATE tbl_customer_policies SET "+
"start_date = sysdate," + // or "start_date = ADD_MONTHS(SYSDATE, 12),"
"next_pay_date = ?,"+
"maturity_date = ?, " +
"modified_at = CURRENT_TIMESTAMP,"+
"modifier = ?, status = ? " +
"WHERE id = ?";
And set all the others parameters like you did before.
Use function TO_DATE (docs)
for example
"UPDATE tbl_customer_policies SET "+
"start_date = TO_DATE(?, 'DD-MON-RR')," +
"next_pay_date = TO_DATE(?, 'DD-MON-RR'),"+
and don't forget pass parameters in format like '27-OCT-98' in this case.
P.S.
I misunderstood the question so try to correct my answer.
The described problem is for limitations of PreparedStatement Java class.
PreparedStatement object is used for storing a reference to some precompiled (and optimized) SQL statement.
So you have to supply only values of parameters for filling them.
Functions must be evaluated before using their results and they aren't able to be placed there.
I guess you can use a workaround with Oracle Structured Types.
You can pass them as reference types with PreparedStatement's setRef() method.
Using functions can be implemented with a wrapper in constructor of object.
I didn't try it but it seems to me it is a possible solution.

Escaping a single quote when using JdbcTemplate

We're using JdbcTemplate to modify our underlying Oracle database. We're doing this by way of the update(String sql) method.
The code looks somehow like the following:
String name = "My name's yellow";
String sql = "update FIELD set NAME = '" + name "' where ID = 10
jdbcTemplate.update(sql);
This causes the error:
java.sql.SQLException: ORA-00933: SQL command not properly ended
The problem is the unescaped ' in the name variable.
What's the most convenient and correct way to escape this character?
Use PreparedStatement. That way you nominate a placeholder and the JDBC driver will perform this correctly by sending the database the statement, plus the parameters as arguments.
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
PreparedStatement updateTotal = con.prepareStatement(updateStatement);
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
The question marks in the above represent the placeholders.
Because these values get passed as parameters, you don't have problems with quoting, and it protects you against SQL injection too.
Try for name :
if ( name.contains("'") ){
name.replaceAll("'", "''");
}

How to change a value in mysql database using conditions from java code?

I'm trying to change a value Dr_status that only contain one int even 0 or 1. So if Dr_status equal to 1 change it to 0 and vice versa.
Here is the code :
String query = "Select Bluetooth_Address FROM dr";
String str = "40D32DBBE665";//in the database I have only two fields in `Bluetooth_Address` column 40D32DBBE665 and another value
String check = "";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
PreparedStatement preparedStmt= con.prepareStatement("update `dr` set `Dr_status` = '1'");
PreparedStatement preparedStmt1= con.prepareStatement("update `dr` set `Dr_status` = '0'");
dbtime = rs.getString(1);
if (dbtime.equals(str)){
check = "Select `Dr_status` From `dr` Where `Bluetooth_Address` = " + " " + str ;
if(check.equals(0)){
preparedStmt.executeUpdate();
}
if(check.equals(1)){
preparedStmt1.executeUpdate();
}
I don't know where is the problem !!! please help.
Thanks in advance.
I give +1 to the answer from #Marcelo Hernández Rishmawy. Instead of testing the condition in Java code, do the test and the update in an SQL expression that converts 0 to 1 and 1 to 0 automatically, for the rows that match your Bluetooth address condition.
I'll also give you a tip that in MySQL, 1 and 0 are integers, but they are also used for true and false. So you can use either of the following tricks to make the statement more compact:
"update `dr` set `Dr_status` = ! `Dr_status` where `Bluetooth_Address = " + str
This trick works too:
"update `dr` set `Dr_status` = 1 - `Dr_status` where `Bluetooth_Address = " + str
It's a nice way to simplify, but FWIW it's specific to MySQL, not standard SQL. Other databases brands use proper boolean values, not 1 and 0.
Re your comment: the error is not related to the solutions above, it's because you're interpolating a string of hex digits. You need to either quote the string, or better yet use a query parameter.
You should learn how to use query parameters in any case, because they're good for writing secure code to defend against SQL injection issues, and it's generally easier and more robust than trying to interpolate variables into SQL query strings.
See Using Prepared Statements at The Java Tutorials.
Use something similar to:
"update `dr` set `Dr_status` = CASE `Dr_status` WHEN '1' THEN '0' ELSE '1' END CASE Where `Bluetooth_Address` = '" + str + "'"
The line if(check.equals(0)){ is invalid. check is a String and will never equal 0

Categories

Resources