JTDS: Unicode parameters using CallableStatement with sendStringParametersAsUnicode=false - java

We have encountered the performance issues described in the JTDS documentation regarding index scans (SQL Server 2000 and upwards), and have therefore had to set the sendStringParametersAsUnicode parameter to false.
This is fine for 99.9% of our cases, however, we have an application that does rely on unicode data in an ntext field. We write to the aforementioned table using a stored procedure, which has an NTEXT parameter. Since changing the above setting, our unicode strings are translated to '?' characters, which is not particularly useful.
I have fiddled with various things, including:
setObject(1, unicode_string, Types.NCLOB); //as well as NVARCHAR
stmt.setUnicodeStream(1, new ByteArrayInputStream(unicode_string.getBytes("UTF16")), unicode_string.length());
setNClob(1, unicode_string);
None of these however work. Any ideas?

One workaround (though its not the correct answer), is to use a Statement rather than a CallableStatement:
stmt = cn.createStatement();
stmt.execute("INSERT INTO test_unicode (my_unicode) VALUES (N'" + input + "')");
This is however presents a significant performance overhead.

Related

Weird Behavior of ResultSet

hello mates hope you having a nice day, i have a really weird problem with getting data from Mysql database in Java , in the method here :
ResultSet tableExistence;
.
.
.
while (tableExistence.next()) {
System.out.println("before : "+tableExistence.getInt(MyHttpServer.COL_ID));
if(tableExistence.getString(MyHttpServer.COL_STATUS).equals(MyHttpServer.STATUS_AVAILABLE)){
System.out.println("after : "+tableExistence.getInt(MyHttpServer.COL_ID));
...
}
weirdly the value of the "before" is for the right value of the id , but after the if method, the value of "after" returns some thing like 1234125151231 , any idea why is this problem ?!!!!
The ResultSet documentation states:
The docs do say "For maximum portability, result set columns within each row should be read in left-to-right order, and each column should be read only once."
So technically, it's somewhat reasonable. However:
I would actually expect that any modern, well-supported database would allow you to access the columns in an arbitrary order, multiple times
Just returning an incorrect value rather than throwing an exception to indicate the problem is pretty nasty
One explanation could be if you're fetching a clob or blob - the driver may decide in that case that "left to right only" is more reasonable, to avoid having to keep too much data in memory. It's still not great that it just returns corrupted data though.
So while this is a possible answer, I would also:
Check that neither the connection nor the ResultSet is being used from multiple threads concurrently
Check that you're using a recent version of the MySQL driver

Oracle JDBC PreparedStatement Ignore Trailing Spaces

I am currently writing a Java web application which interfaces with an Oracle database. I am using PreparedStatements because Hibernate would complicate things too much.
Due to a bug in the program which is writing to the database, the field I need to search for has trailing spaces written to the value. I have surrounded the value with quotation marks to demonstrate the whitespace.
"testFTP_receipt521 "
When I do a select query with SQLDeveloper, I am able to get a result when I run:
...and yfs_organization.ORGANIZATION_KEY='testFTP_receipt521';
(no whitespace)
However, when I use a PreparedStatement, I get no results when I try:
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521");
(no whitespace)
and when I try:
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521 ");
(with whitespace)
Are there any ways that I can query for this result with a PreparedStatement, or should I try another approach?
Thanks for all your help.
Due to a bug in the program which is writing to the database, the field I need to search for has trailing spaces
Maybe, given the circumstances, and if your version of Oracle is recent enough, you might consider adding a virtual column to your table containing the correct value?
ALTER TABLE yfs_organization ADD (
ORGANIZATION_KEY_FIXED VARCHAR(80)
GENERATED ALWAYS AS (TRIM(ORGANIZATION_KEY)) VIRTUAL
);
Then in your code, the only change will be to use the ORGANIZATION_KEY_FIXED to query the DB:
SELECT ID,ORGANIZATION_KEY_FIXED
FROM yfs_organization
WHERE ORGANIZATION_KEY_FIXED='testFTP_receipt521'
(try it on http://sqlfiddle.com/#!4/8251d/1)
This might avoid to scatter around your application the code required to work around that bug. And might ease the transition once it will be fixed.
As an added benefice, you could add index on virtual columns if you need too.
Maybe you can use it like this...
...and yfs_organization.ORGANIZATION_KEY like '%testFTP_receipt521%';
this way returns you all reg where contains 'testFTP_receipt521' independently of whitespace.
Antoher thing that i saw in your code in this part
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521");
i thing this is the correct way
...and yfs_organization.ORGANIZATION_KEY='?'");
you need to put quotes around the criteria
If you have the ability to modify the query, you can TRIM(...) the column value and perform the comparison. For example:
...and TRIM(yfs_organization.ORGANIZATION_KEY)=?");
Hope it helps.

Getting question marks when inserting Hebrew characters into a MySQL table

I'm using Netbeans building a web application using Java, JSP that handle a database with Hebrew fields.
The DDL is as follows:
String cityTable = "CREATE TABLE IF NOT EXISTS hebrew_test.table ("
+"id int(11) NOT NULL AUTO_INCREMENT,"
+"en varchar(30) NOT NULL,"
+"he varchar(30) COLLATE utf8_bin NOT NULL,"
+"PRIMARY KEY (id)"
+") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1;";
String insert = "INSERT INTO hebrew_test.table (en, he) VALUES ('A','a')";
String insert2 = "INSERT INTO hebrew_test.table (en, he) VALUES ('B','ב')";
String insert3 = "INSERT INTO hebrew_test.table (en, he) VALUES ('C','אבג')";
executeSQLCommand(cityTable);
executeSQLCommand(insert);
executeSQLCommand(insert2);
executeSQLCommand(insert3);
The output tabel I get:
1 A a
2 B ?
3 C ???
Instead of:
1 A a
2 B ב
3 C אבג
I tried Hebrew appears as question marks in Netbeans, but that isn't the same problem. I get the question marks in the table.
Also I defined the table to be in UTF8_bin as you can see in the above code.
You need to tell the JDBC driver to use UTF-8 encoding while decoding the characters representing the SQL query to bytes. You can do that by adding useUnicode=yes and characterEncoding=UTF-8 query parameters to the JDBC connection URL.
jdbc:mysql://localhost:3306/db_name?useUnicode=yes&characterEncoding=UTF-8
It will otherwise use the operating system platform default charset. The MySQL JDBC driver is itself well aware about the encoding used in both the client side (where the JDBC code runs) and the server side (where the DB table is). Any character which is not covered by the charset used by the DB table will be replaced by a question mark.
See also:
Spring Encoding with CharacterEncodingFilter in web.xml
You're including your values directly into the SQL. That's always a bad idea. Use a PreparedStatement, parameterized SQL, and set the values as parameters. It may not fix the problem - but it's definitely the first thing to attempt, as you should be using parameterized SQL anyway. (Parameterized SQL avoids SQL injection attacks, separates code from data, and avoids unnecessary conversions.)
Next, you should work out exactly where the problem is really occurring:
Make sure that the value you're trying to insert is correct.
Check that the value you retrieve is correct.
Check what's in your web response using Wireshark - check the declared encoding and what's in the actual data
When checking the values, you should iterate over each character in the string and print out the value as a UTF-16 code unit (either use toCharArray() or use charAt() in a loop). Just printing the value to the console leaves too much chance of other problems.
EDIT: For a little context of why I wrote this as an answer:
In my experience, including string values as parameters rather than directly into SQL can sometimes avoid such issues (and is of course better for security reasons etc).
In my experience, diagnosing whether the problem is at the database side or the web side is also important. This diagnosis is best done via logging the exact UTF-16 code units being used, not just strings (as otherwise further encoding issues during logging or console output can occur).
In my experience, problems like this can easily occur at either insert or read code paths.
All of this is important as a way of moving the OP forward, not just in a comment-like request for more information.

Blacklist filtering data for SQL Keywords

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...

JDBC, MySQL: getting bits into a BIT(M!=1) column

I'm new to using JDBC + MySQL.
I have several 1/0 values which I want to stick into a database with a PreparedStatement. The destination column is a BIT(M!=1). I'm unclear on which of the setXXX methods to use. I can find the references for what data comes out as easily enough, but how it goes in is eluding me.
The values effectively live as an ordered collection of booleans in the objects used by the application. Also, I'll occasionally be importing data from flat text files with 1/0 characters.
To set a BIT(M) column in MySQL
For M==1
setBoolean(int parameterIndex, boolean x)
From the javadoc
Sets the designated parameter to the
given Java boolean value. The driver
converts this to an SQL BIT value when
it sends it to the database.
For M>1
The support for BIT(M) where M!=1 is problematic with JDBC as BIT(M) is only required with "full" SQL-92 and only few DBs support that.
Check here Mapping SQL and Java Types: 8.3.3 BIT
The following works for me with MySQL (at least with MySQL 5.0.45, Java 1.6 and MySQL Connector/J 5.0.8)
...
PreparedStatement insert = con.prepareStatement(
"INSERT INTO bittable (bitcolumn) values (b?)"
);
insert.setString(1,"111000");
...
This uses the special b'110101010' syntax of MySQL to set the value for BIT columns.
You can use get/setObject with a byte array (byte[]). 8 bits are packed into each byte with the least significant bit being in the last array element.

Categories