MyBatis/Ibatis中#和$的區別
1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,如果傳入的值是111,那么解析成sql時的值為order by "111", 如果傳入的值是id,則解析成的sql為order by "id".
2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,如果傳入的值是111,那么解析成sql時的值為order by user_id, 如果傳入的值是id,則解析成的sql為order by id.
3. #方式能夠很大程度防止sql注入。
4.$方式無法防止Sql注入。
5.$方式一般用於傳入數據庫對象,例如傳入表名.
6.一般能用#的就別用$.
附SQL注入DEMO:
mybatis的sql中使用$會出現sql注入示例:
模擬簡單登錄場景:
頁面代碼:
function login(){ //sql注入 var user = { username : "'李雪雷3' or 1=1", password : "'ab0715'", } $.ajax({ url: '/api/test/login.json', type: "POST", data: JSON.stringify(user),//將對象序列化成JSON字符串 dataType: "json", contentType : 'application/json;charset=utf-8', //設置請求頭信息 async: false, success: function (result) { debugger; $("#dis").html(result.data); }, error: function (xhr, ajaxOptions, thrownError) { debugger; alert("出錯了"); } }) }
<input type="button" onclick="login()" value="登錄" name="sunmitBtn">
controller:
@RequestMapping("/login") @ResponseBody public BaseResponse login(HttpServletRequest request, HttpServletResponse response, @RequestBody UserParam userParam){ try{ User user = userService.selectByCon(userParam); if(user != null){ return BaseResponse.successCustom().setData("登錄成功").build(); } return BaseResponse.successCustom().setData("登錄失敗").build(); //return BaseResponse.successCustom().setData(returnVal).build(); }catch (Exception e){ e.printStackTrace(); return BaseResponse.failedCustom("系統異常").build(); } }
service接口:
User selectByCon(UserParam userParam);
service實現類:
@Override public User selectByCon(UserParam userParam) { User user = userMapper.selectByCon(userParam); return user; }
mapper接口:
User selectByCon(UserParam userParam);
mapper.xml文件:
使用$的sql:
<select id="selectByCon" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user2 where 1=1 <if test="username != null" > and username = ${username} </if> <if test="password != null" > and password = ${password} </if> </select>
數據庫用戶表user2數據:
運行程序:
編譯后的sql:
SELECT username, PASSWORD FROM user2 WHERE 1 = 1 AND username = '李雪雷3' OR 1 = 1 AND PASSWORD = 'ab0715'
執行結果:
登錄成功:
利用了sql注入漏洞騙過了口令從而登錄成功。在賬號正確的前提下,密碼不管輸入什么都能成功登錄。
說明:
如果使用$寫的sql,我們可以利用sql注入漏洞來進行攻擊。如果進行表的刪除及數據修改sql注入,而數據庫沒有備份數據,那將是毀滅性的。
=============================================
使用#的sql:
<select id="selectByCon" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user2 where 1=1 <if test="username != null" > and username = #{username} </if> <if test="password != null" > and password = #{password} </if> </select>
運行程序:
編譯后的sql:
SELECT username, PASSWORD FROM user2 WHERE 1 = 1 AND username = ? AND PASSWORD = ?
加入參數,翻譯過來就是:
SELECT username, PASSWORD FROM user2 WHERE 1 = 1 AND username = "'李雷雷3' or 1=1" AND PASSWORD = "'ab0715'"
執行結果:
說明:
使用#的sql進行了預編譯,用?接受參數。如果是字符串的參數,則使用" "雙引號括起來,有效防止了sql注入。
=========================================
補充:
預編譯的好處:
在執行SQL命令時,有二種選擇:可以使用PreparedStatement對象,也可以使用Statement對象。
而熟悉JDBC編程的大俠們都會選擇使用PreparedStatement對象,主要因為使用預編譯對象PreparedStatement時,有以下幾個優點:
1、效率高
PreparedStatement可以盡可能的提高訪問數據庫的性能,我們都知道數據庫在處理SQL語句時都有一個預編譯的過程,而預編譯對象就是把一些格式固定的SQL編譯后,存放在內存池中即數據庫緩沖池,當我們再次執行相同的SQL語句時就不需要預編譯的過程了,只需DBMS運行SQL語句。所以當你需要執行Statement對象多次的時候,PreparedStatement對象將會大大降低運行時間,特別是的大型的數據庫中,它可以有效的也加快了訪問數據庫的速度。
2、大大提高代碼的可讀性和可維護性
例如我們在向數據庫插入數據:
一種是使用Statement對象
java.sql.Statement stmt=conn.createStatement();
stmt.executeUpdate("insert into student (name,id,number,count) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')"); 另一種是使用PreparedStatement對象
String sql ="insert into student values(null,?,?,?)";
java.sql.PreparedStatement pstmt=conn.preparedStatement(sql);
pstmt.setString(1,var1); pstmt.setString(2,var2); pstmt.setString(3,var3); pstmt.setString(4,var4); pstmt.executeUpdate();
使用占位符?代替
將參數與SQL語句分離出來,這樣就可以方便對程序的更改和延續,同樣,也可以減少不必要的錯誤。
3、開源防止SQL注入(最主要的)
什么時候使用預編譯語句?
一般是在需要反復使用一個SQL語句時才使用預編譯語句,預編譯語句常常放在一個fo r或者while循環里面使用,通過反復設置參數從而多次使用該SQL語句。為了防止SQL注入漏洞,在某些數據操作中也使用預編譯語句。
參考鏈接:https://blog.csdn.net/mawming/article/details/45043015
參考鏈接:https://www.cnblogs.com/super-chao/p/8421505.html