Bug #41730 SQL Injection when using U+00A5
Submitted: 24 Dec 2008 11:11 Modified: 26 May 2009 10:45
Reporter: Sadao Hiratsuka (Basic Quality Contributor) Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S1 (Critical)
Version:5.1.7 OS:Any
Assigned to: Jess Balint CPU Architecture:Any
Tags: Connector/J

[24 Dec 2008 11:11] Sadao Hiratsuka
Description:
SQL Injection occurs when

- using Connector/J
- client-side Prepared Statement
- String has U+00A5
- characterEncoding is not UTF-8

How to repeat:
MySQL Server 5.1.30
Connector/J 5.1.7

- sample data
mysql> select empno, ename from emp;
+-------+--------+
| empno | ename  |
+-------+--------+
|  7369 | smith  |
|  7499 | allen  |
|  7521 | ward   |
|  7566 | jones  |
|  7654 | martin |
|  7698 | blake  |
|  7782 | clark  |
|  7788 | scott  |
|  7839 | king   |
|  7844 | turner |
|  7876 | adams  |
|  7900 | james  |
|  7902 | ford   |
|  7934 | miller |
+-------+--------+
14 rows in set (0.00 sec)

- sample code
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class InjectionTest {
	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/scott?characterEncoding=Windows-31J",
					"scott", "tiger");
			PreparedStatement pstmt = conn.prepareStatement("select empno from emp where ename = ?");
			pstmt.setString(1, "\u00a5' or 1 = 1#");

			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				System.out.println(rs.getInt(1));
			}

			rs.close();
			pstmt.close();
			conn.close();

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

- results
7369
7499
7521
7566
7654
7698
7782
7788
7839
7844
7876
7900
7902
7934

Suggested fix:
escape U+00A5 suitably.
[26 Dec 2008 2:25] Sadao Hiratsuka
After post, I learned that escaping U+00A5 thoughtlessly has side effects.

Now suggested fix I think is:
validate characters *after* converting them to 'characterEncoding'.
[26 Dec 2008 4:45] Yoshinori Matsunobu
I wrote a prototype patch.

PreparedStatement#setString()
......
  for (int i = 0; i < stringLength; ++i) {
    char c = x.charAt(i);

    switch (c) {
    case 0: /* Must be escaped for 'mysql' */
      buf.append('\\');
      buf.append('0');
......
    default:
    //add the following
      String characterEncoding = this.connection.getEncoding();
      try{
        if(characterEncoding != null &&
          String.valueOf(c).getBytes(characterEncoding)[0] == 0x5c){
          buf.append('\\');
          buf.append('\\');
        }else {
          buf.append(c);
        }
      }catch(UnsupportedEncodingException e){}
      break;
......

or 
    case '\u00a5':
      String characterEncoding = this.connection.getEncoding();
      try{
        if(characterEncoding != null &&
          String.valueOf(c).getBytes(characterEncoding)[0] == 0x5c){
          buf.append('\\');
          buf.append('\\');
        }
      }catch(UnsupportedEncodingException e){}
      break;

We need to care about not only U+00A5, but also U+20A9 and possibly others...
[19 Feb 2009 7:28] Tonci Grgin
Sadao, Yoshinori, we are having internal discussion about this bug and it's not forgotten.

Thanks.
[24 Feb 2009 3:14] Jess Balint
fix + test

Attachment: bug41730.diff (text/x-diff), 3.98 KiB.

[15 May 2009 22:46] Jess Balint
Pushed as rev 792. Will be released in 5.1.8.
[26 May 2009 10:45] Tony Bedford
An entry has been added to the 5.1.8 changelog:

SQL injection was possible when using a string containing U+00A5 in a client-side prepared statement, and the character set being used was SJIS/Windows-31J.