Mybatis是如何實現SQL防注入的

Mybatis這個框架在平常開發中用的不少,好比面試中常常有一個問題:$#的區別,它們的區別是使用#能夠防止SQL注入,今天就來看一下它是如何實現SQL注入的。java

什麼是SQL注入

在討論怎麼實現以前,首先了解一下什麼是SQL注入,咱們有一個簡單的查詢操做:根據id查詢一個用戶信息。它的sql語句應該是這樣:select * from user where id =。咱們根據傳入條件填入id進行查詢。面試

若是正常操做,傳入一個正常的id,好比說2,那麼這條語句變成select * from user where id =2。這條語句是能夠正常運行而且符合咱們預期的。sql

可是若是傳入的參數變成'' or 1=1,這時這條語句變成select * from user where id = '' or 1=1。讓咱們想一下這條語句的執行結果會是怎麼?它會將咱們用戶表中全部的數據查詢出來,顯然這是一個大的錯誤。這就是SQL注入。數據庫

Mybatis如何防止SQL注入

在開頭講過,可使用#來防止SQL注入,它的寫法以下:安全

<select id="safeSelect" resultMap="testUser">
   SELECT * FROM user where id = #{id}
</select>

在mybatis中查詢還有一個寫法是使用$,它的寫法以下:mybatis

<select id="unsafeSelect" resultMap="testUser">
   select * from user where id = ${id}
</select>

當咱們在外部對這兩個方法繼續調用時,發現若是傳入安全的參數時,二者結果並沒有不一樣,若是傳入不安全的參數時,第一種使用#的方法查詢不到結果(select * from user where id = '' or 1=1),但這個參數在第二種也就是$下會獲得所有的結果。框架

而且若是咱們將sql進行打印,會發現添加#時,向數據庫執行的sql爲:select * from user where id = ' \'\' or 1=1 ',它會在咱們的參數外再加一層引號,在使用$時,它的執行sql是select * from user where id = '' or 1=1code

棄用$能夠嗎

咱們使用#也能完成$的做用,而且使用$還有危險,那麼咱們之後不使用$不就好了嗎。開發

並非,它只是在咱們這種場景下會有問題,可是在有一些動態查詢的場景中仍是有不可代替的做用的,好比,動態修改表名select * from ${table} where id = #{id}。咱們就能夠在返回信息一致的狀況下進行動態的更改查詢的表,這也是mybatis動態強大的地方。get

如何實現SQL注入的,不用Mybatis怎麼實現

其實Mybatis也是經過jdbc來進行數據庫鏈接的,若是咱們看一下jdbc的使用,就能夠獲得這個緣由。

#使用了PreparedStatement來進行預處理,而後經過set的方式對佔位符進行設置,而$則是經過Statement直接進行查詢,當有參數時直接拼接進行查詢。

因此說咱們可使用jdbc來實現SQL注入。

看一下這兩個的代碼:

public static void statement(Connection connection) {
  System.out.println("statement-----");
  String selectSql = "select * from user";
  // 至關於mybatis中使用$,拿到參數後直接拼接
  String unsafeSql = "select * from user where id = '' or 1=1;";
  Statement statement = null;
  try {
    statement = connection.createStatement();
  } catch (SQLException e) {
    e.printStackTrace();
  }
  try {
    ResultSet resultSet = statement.executeQuery(selectSql);
    print(resultSet);
  } catch (SQLException e) {
    e.printStackTrace();
  }
  System.out.println("---****---");
  try {
    ResultSet resultSet = statement.executeQuery(unsafeSql);
    print(resultSet);
  } catch (SQLException e) {
    e.printStackTrace();
  }
}

public static void preparedStatement(Connection connection) {
  System.out.println("preparedStatement-----");
  String selectSql = "select * from user;";
  //至關於mybatis中的#,先對要執行的sql進行預處理,設置佔位符,而後設置參數
  String safeSql = "select * from user where id =?;";
  PreparedStatement preparedStatement = null;
  try {
    preparedStatement = connection.prepareStatement(selectSql);
    ResultSet resultSet = preparedStatement.executeQuery();
    print(resultSet);
  } catch (SQLException e) {
    e.printStackTrace();
  }
  System.out.println("---****---");
  try {
    preparedStatement = connection.prepareStatement(safeSql);
    preparedStatement.setString(1," '' or 1 = 1 ");
    ResultSet resultSet = preparedStatement.executeQuery();
    print(resultSet);
  } catch (SQLException e) {
    e.printStackTrace();
  }
}

public static void print(ResultSet resultSet) throws SQLException {
  while (resultSet.next()) {
    System.out.print(resultSet.getString(1) + ", ");
    System.out.print(resultSet.getString("name") + ", ");
    System.out.println(resultSet.getString(3));
  }
}

總結

  • Mybatis中使用#能夠防止SQL注入,$並不能防止SQL注入
  • Mybatis實現SQL注入的原理是調用了jdbc中的PreparedStatement來進行預處理。

    本文由博客一文多發平臺 OpenWrite 發佈! 博主郵箱:liunaijie1996@163.com,有問題能夠郵箱交流。

相關文章
相關標籤/搜索