sql占位符

来源:互联网 发布:淘宝开店程序 编辑:程序博客网 时间:2024/06/05 19:15

http://blog.csdn.net/yan465942872/article/details/6753957


这两天在上课时被同学拿了一段代码问我,这段代码有什么问题,我看了一会说:Connection和PreparedStatement都没关。他说不止这方面的问题,还有sql注入的问题,我就坚决的说使用了占位符不存在sql注入的问题,但是他提出了一种情况,在我看来也很有道理的情况。

[java] view plaincopy
  1. pstmt = conn.prepareStatement("delete from user where user.id=?");  
pstmt.setString(1, "w");他认为如果把代码写成这样就有注入问题了
[java] view plaincopy
  1. pstmt = conn.prepareStatement("delete from user where user.id=?");  
  2. pstmt.setString(1"w' or '2'='2");  

当时我看了只能告诉他一定不存在注入问题,因为在我的想法中我一直记得的是用占位符能解决注入问题,至于怎么解决的就不知道了,看了上面的代码也很有道理,感觉setString后的sql语句应该是
[sql] view plaincopy
  1. delete from user where user.id='w' or '2'='2';  


回到宿舍我专门写了程序测试一下,事实证明并不想我们想的这样,的确使用占位符不存在注入问题,所以解释是在执行的时候把一些字符给转义了,但这个转义的过程是在什么地方转义的呢,把上面的sql语句在mysql控制台上运行一下,查看一下数据看到所有数据都被删除完,那只能解释成在java程序中转义的,于是我就去看java的源代码,发现在java源码中PreparedStatement只是一个接口,而且是没有子类的接口,我就很纳闷,没实现怎么用的?所以一定有实现的地方,去网上查了一下,jdk直提供接口,而具体实现是由数据库厂商实现的,我们用的就是数据库厂商实现的类。于是我就又去查mysql的jar包源码,发现有个PreparedStatement实现了jdk中的PreparedStatement了。里面的setString方法如下实现:

[java] view plaincopy
  1. public void setString(int parameterIndex, String x) throws SQLException {  
  2.         // if the passed string is null, then set this column to null  
  3.         if (x == null) {  
  4.             setNull(parameterIndex, Types.CHAR);  
  5.         } else {  
  6.             StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));  
  7.             buf.append('\'');  
  8.   
  9.             int stringLength = x.length();  
  10.   
  11.             //  
  12.             // Note: buf.append(char) is _faster_ than  
  13.             // appending in blocks, because the block  
  14.             // append requires a System.arraycopy()....  
  15.             // go figure...  
  16.             //  
  17.             for (int i = 0; i < stringLength; ++i) {  
  18.                 char c = x.charAt(i);  
  19.   
  20.                 switch (c) {  
  21.                 case 0/* Must be escaped for 'mysql' */  
  22.                     buf.append('\\');  
  23.                     buf.append('0');  
  24.   
  25.                     break;  
  26.   
  27.                 case '\n'/* Must be escaped for logs */  
  28.                     buf.append('\\');  
  29.                     buf.append('n');  
  30.   
  31.                     break;  
  32.   
  33.                 case '\r':  
  34.                     buf.append('\\');  
  35.                     buf.append('r');  
  36.   
  37.                     break;  
  38.   
  39.                 case '\\':  
  40.                     buf.append('\\');  
  41.                     buf.append('\\');  
  42.   
  43.                     break;  
  44.   
  45.                 case '\'':  
  46.                     buf.append('\\');  
  47.                     buf.append('\'');  
  48.   
  49.                     break;  
  50.   
  51.                 case '"'/* Better safe than sorry */  
  52.                     if (this.usingAnsiMode) {  
  53.                         buf.append('\\');  
  54.                     }  
  55.   
  56.                     buf.append('"');  
  57.   
  58.                     break;  
  59.   
  60.                 case '\032'/* This gives problems on Win32 */  
  61.                     buf.append('\\');  
  62.                     buf.append('Z');  
  63.   
  64.                     break;  
  65.   
  66.                 default:  
  67.                     buf.append(c);  
  68.                 }  
  69.             }  
  70.   
  71.             buf.append('\'');  
  72.   
  73.             String parameterAsString = buf.toString();  
  74.   
  75.             byte[] parameterAsBytes = null;  
  76.   
  77.             if (!this.isLoadDataQuery) {  
  78.                 parameterAsBytes = StringUtils.getBytes(parameterAsString,  
  79.                         this.charConverter, this.charEncoding, this.connection  
  80.                                 .getServerCharacterEncoding(), this.connection  
  81.                                 .parserKnowsUnicode());  
  82.             } else {  
  83.                 // Send with platform character encoding  平台字符编码
  84.                 parameterAsBytes = parameterAsString.getBytes();  
  85.             }  
  86.   
  87.             setInternal(parameterIndex, parameterAsBytes);  
  88.         }  
  89.     }  


到此就告一段落,可以发现在setString时最外面的单引号被转义了,也就是说setString后的sql语句是这样的

[sql] view plaincopy
  1. delete from user where user.id=\'w' or '2'='2\';  
而且仔细看会发现在setString中是一个字符一个字符的解析,该转义的都已经转义,正如他一句注释中写的Better safe than sorry.所以最终,占位符确实不存在注入问题
0 0
原创粉丝点击