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:
- buf.append('\\');
- buf.append('0');
- break;
- case '\n':
- 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 '"':
- if (this.usingAnsiMode) {
- buf.append('\\');
- }
- buf.append('"');
- break;
- case '\032':
- 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注入攻击呢?