占位符,SQL註入?
這兩天在上課時被同學拿了一段代碼問我,這段代碼有什麽問題,我看了一會說:Connection和PreparedStatement都沒關。他說不止這方面的問題,還有sql註入的問題,我就堅決的說使用了占位符不存在sql註入的問題,但是他提出了一種情況,在我看來也很有道理的情況。
pstmt = conn.prepareStatement("delete from user where user.id=?"); pstmt.setString(1, "w");
他認為如果把代碼寫成這樣就有註入問題了
pstmt = conn.prepareStatement("delete from user where user.id=?"); pstmt.setString(1, "w‘ or ‘2‘=‘2");
當時我看了只能告訴他一定不存在註入問題,因為在我的想法中我一直記得的是用占位符能解決註入問題,至於怎麽解決的就不知道了,看了上面的代碼也很有道理,感覺setString後的sql語句應該是
delete from user where user.id=‘w‘ or ‘2‘=‘2‘;
回到宿舍我專門寫了程序測試一下,事實證明並不想我們想的這樣,的確使用占位符不存在註入問題,所以解釋是在執行的時候把一些字符給轉義了,但這個轉義的過程是在什麽地方轉義的呢,把上面的sql語句在mysql控制臺上運行一下,查看一下數據看到所有數據都被刪除完,那只能解釋成在java程序中轉義的,於是我就去看java的源代碼,發現在java源碼中PreparedStatement只是一個接口,而且是沒有子類的接口,我就很納悶,沒實現怎麽用的?所以一定有實現的地方,去網上查了一下,jdk直提供接口,而具體實現是由數據庫廠商實現的,我們用的就是數據庫廠商實現的類。於是我就又去查mysql的jar包源碼,發現有個PreparedStatement實現了jdk中的PreparedStatement了。裏面的setString方法如下實現:
public void setString(int parameterIndex, String x) throws SQLException { // if the passed string is null, then set this column to null if (x == null) { setNull(parameterIndex, Types.CHAR); } else { StringBuffer buf = new StringBuffer((int) (x.length() * 1.1)); buf.append(‘\‘‘); int stringLength = x.length(); // // 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; default: buf.append(c); } } buf.append(‘\‘‘); String parameterAsString = buf.toString(); byte[] parameterAsBytes = null; if (!this.isLoadDataQuery) { parameterAsBytes = StringUtils.getBytes(parameterAsString, this.charConverter, this.charEncoding, this.connection .getServerCharacterEncoding(), this.connection .parserKnowsUnicode()); } else { // Send with platform character encoding parameterAsBytes = parameterAsString.getBytes(); } setInternal(parameterIndex, parameterAsBytes); } }
到此就告一段落,可以發現在setString時最外面的單引號被轉義了,也就是說setString後的sql語句是這樣的
delete from user where user.id=‘w\‘ or \‘2\‘=\‘2‘;
而且仔細看會發現在setString中是一個字符一個字符的解析,該轉義的都已經轉義,正如他一句註釋中寫的Better safe than sorry.所以最終,占位符確實不存在註入問題
轉載於http://blog.csdn.net/yan465942872/article/details/6753957
占位符,SQL註入?