接上文
http://blog.itpub.net/29254281/viewspace-1318239/
领导让开发同学鼓捣一个可配置化的后台.
又回到了原来的问题
如果要灵活,很多参数要从前端页面传过来,有SQL注入的风险.
如果参数化SQL,又很难做到灵活..
先看一个注入的例子:
-
import java.sql.Connection;
-
import java.sql.DriverManager;
-
import java.sql.ResultSet;
-
import java.sql.SQLException;
-
import java.sql.Statement;
-
-
public class Test {
-
public static void main(String[] args) throws SQLException {
-
String para="/index.html' union all select * from probe -- ";
-
Connection conn = DriverManager.getConnection("jdbc:MySQL://127.0.0.1:3306/mvbox", "xx", "xx");
-
-
Statement ps=conn.createStatement();
-
-
ResultSet rs = ps.executeQuery("select * from probe where path='"+para+"'");
-
while (rs.next()) {
-
System.out.println(rs.getString("host")+":"+rs.getString("path"));
-
}
-
rs.close();
-
ps.close();
-
conn.close();
-
}
-
}
如果要避免这种风险,可以选择参数化
-
import java.sql.Connection;
-
import java.sql.DriverManager;
-
import java.sql.PreparedStatement;
-
import java.sql.ResultSet;
-
import java.sql.SQLException;
-
-
public class Test {
-
public static void main(String[] args) throws SQLException {
-
String para="/index.html' union all select * from probe -- ";
-
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mvbox", "xx", "xx");
-
PreparedStatement ps=conn.prepareStatement("select * from probe where path=?");
-
ps.setString(1, para);
-
ResultSet rs=ps.executeQuery();
-
while (rs.next()) {
-
System.out.println(rs.getString("host")+":"+rs.getString("path"));
-
}
-
rs.close();
-
ps.close();
-
conn.close();
-
}
-
}
为何参数化可以防止注入?
作为MySQL JDBC驱动来说(5.1.31),其实就是对敏感字符做了转义.
观察 com.mysql.jdbc.PreparedStatement 的 setString方法
可以看到有如下的替换过程
-
String parameterAsString = x;
-
boolean needsQuoted = true;
-
-
if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
-
needsQuoted = false; // saves an allocation later
-
-
StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
-
-
buf.append('\'');
-
-
//
-
// Note: buf.append(char) is _faster_ than
-
// appending in blocks, because the block
-
// append requires a System.arraycopy()....
-
// go figure...
-
//
-
-
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');
-
-
break;
-
-
case '\n': /* Must be escaped for logs */
-
buf.append('\\');
-
buf.append('n');
-
-
break;
-
-
case '\r':
-
buf.append('\\');
-
buf.append('r');
-
-
break;
-
-
case '\\':
-
buf.append('\\');
-
buf.append('\\');
-
-
break;
-
-
case '\'':
-
buf.append('\\');
-
buf.append('\'');
-
-
break;
-
-
case '"': /* Better safe than sorry */
-
if (this.usingAnsiMode) {
-
buf.append('\\');
-
}
-
-
buf.append('"');
-
-
break;
-
-
case '\032': /* This gives problems on Win32 */
-
buf.append('\\');
-
buf.append('Z');
-
-
break;
-
-
case '\u00a5':
-
case '\u20a9':
-
// escape characters interpreted as backslash by mysql
-
if(charsetEncoder != null) {
-
CharBuffer cbuf = CharBuffer.allocate(1);
-
ByteBuffer bbuf = ByteBuffer.allocate(1);
-
cbuf.put(c);
-
cbuf.position(0);
-
charsetEncoder.encode(cbuf, bbuf, true);
-
if(bbuf.get(0) == '\\') {
-
buf.append('\\');
-
}
-
}
-
// fall through
-
-
default:
-
buf.append(c);
-
}
-
}
-
-
buf.append('\'');
-
-
parameterAsString = buf.toString();
-
}
是不是可以使用 iBatis 的 $ 方式 增加灵活性,而在参数进入iBatis之前,手工进行一下敏感字符替换,而防止SQL注入攻击呢?
文章题目:再看ibatisOrderBy注入问题
本文来源:
http://bzwzjz.com/article/gphchc.html