日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

【转】从源码分析PreparedStatement是如何防止SQL注入的?

發(fā)布時間:2024/10/8 数据库 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】从源码分析PreparedStatement是如何防止SQL注入的? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

為什么在Java中PreparedStatement能夠有效防止SQL注入?這可能是每個Java程序員思考過的問題。

?

首先我們來看下直觀的現(xiàn)象(注:需要提前打開mysql的SQL文日志)

1. 不使用PreparedStatement的set方法設置參數(shù)(效果跟Statement相似,相當于執(zhí)行靜態(tài)SQL)

String param = "'test' or 1=1"; String sql = "select file from file where name = " + param; // 拼接SQL參數(shù) PreparedStatement preparedStatement = connection.prepareStatement(sql); ResultSet resultSet = preparedStatement.executeQuery(); System.out.println(resultSet.next());

輸出結果為true,DB中執(zhí)行的SQL為

-- 永真條件1=1成為了查詢條件的一部分,可以返回所有數(shù)據(jù),造成了SQL注入問題 select file from file where name = 'test' or 1=1

?

2.?使用PreparedStatement的set方法設置參數(shù)

String param = "'test' or 1=1"; String sql = "select file from file where name = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, param); ResultSet resultSet = preparedStatement.executeQuery(); System.out.println(resultSet.next());

輸出結果為false,DB中執(zhí)行的SQL為

select file from file where name = '\'test\' or 1=1'

我們可以看到輸出的SQL文是把整個參數(shù)用引號包起來,并把參數(shù)中的引號作為轉義字符,從而避免了參數(shù)也作為條件的一部分

?


?

接下來我們分析下源碼(以mysql驅(qū)動實現(xiàn)為例)

打開java.sql.PreparedStatement通用接口,看到如下注釋,了解到PreparedStatement就是為了提高statement(包括SQL,存儲過程等)執(zhí)行的效率。

An object that represents a precompiled SQL statement. A SQL statement is precompiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times.

那么,什么是所謂的“precompiled SQL statement”呢?

回答這個問題之前需要先了解下一個SQL文在DB中執(zhí)行的具體步驟:

  • Convert given SQL query into DB format -- 將SQL語句轉化為DB形式(語法樹結構)
  • Check for syntax -- 檢查語法
  • Check for semantics -- 檢查語義
  • Prepare execution plan -- 準備執(zhí)行計劃(也是優(yōu)化的過程,這個步驟比較重要,關系到你SQL文的效率,準備在后續(xù)文章介紹)
  • Set the run-time values into the query -- 設置運行時的參數(shù)
  • Run the query and fetch the output -- 執(zhí)行查詢并取得結果
  • 而所謂的“precompiled SQL statement”,就是同樣的SQL文(包括不同參數(shù)的),1-4步驟只在第一次執(zhí)行,所以大大提高了執(zhí)行效率(特別是對于需要重復執(zhí)行同一SQL的)

    ?

    言歸正傳,回到source中,我們重點關注一下setString方法(因為其它設置參數(shù)的方法諸如setInt,setDouble之類,編譯器會檢查參數(shù)類型,已經(jīng)避免了SQL注入。)

    查看mysql中實現(xiàn)PreparedStatement接口的類com.mysql.jdbc.PreparedStatement中的setString方法(部分代碼)

    public void setString(int parameterIndex, String x) throws SQLException {synchronized (checkClosed().getConnectionMutex()) {// if the passed string is null, then set this column to nullif (x == null) {setNull(parameterIndex, Types.CHAR);} else {checkClosed();int stringLength = x.length();if (this.connection.isNoBackslashEscapesSet()) {// Scan for any nasty chars// 判斷是否需要轉義處理(比如包含引號,換行等字符)boolean needsHexEscape = isEscapeNeededForString(x, stringLength); // 如果不需要轉義,則在兩邊加上單引號if (!needsHexEscape) {byte[] parameterAsBytes = null;StringBuilder quotedString = new StringBuilder(x.length() + 2);quotedString.append('\'');quotedString.append(x);quotedString.append('\'');...} else {...}String parameterAsString = x;boolean needsQuoted = true;// 如果需要轉義,則做轉義處理if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {...

    從上面加紅色注釋的可以明白為什么參數(shù)會被單引號包裹,并且類似單引號之類的特殊字符會被轉義處理,就是因為這些代碼的控制避免了SQL注入。?

    這里只對SQL注入相關的代碼進行解讀,如果在setString前后輸出預處理語句(preparedStatement.toString()),會發(fā)現(xiàn)如下輸出

    Before bind: com.mysql.jdbc.JDBC42PreparedStatement@b1a58a3: select file from file where name = ** NOT SPECIFIED ** After bind: com.mysql.jdbc.JDBC42PreparedStatement@b1a58a3: select file from file where name = '\'test\' or 1=1'

    編程中建議大家使用PrepareStatement + Bind-variable的方式避免SQL注入

    大家有什么其它的看法,歡迎留下評論!

    參考:https://stackoverflow.com/questions/30587736/what-is-pre-compiled-sql-statement

    總結

    以上是生活随笔為你收集整理的【转】从源码分析PreparedStatement是如何防止SQL注入的?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。