網頁闖關游戲(riddle webgame)--SQL注入的潘多拉魔盒


 

前言:
  之前編寫了一個網頁闖關游戲(類似Riddle Game), 除了希望大家能夠體驗一下我的游戲外. 也願意分享編寫這個網頁游戲過程中, 學到的一些知識.
  web開發初學者往往會忽視一些常見的漏洞, 比如SQL注入攻擊, XSS攻擊. 本文將簡述SQL注入攻擊的原理, 並分享下關卡設計, 其在打開潘多拉魔盒的情況下, 又能很好地限制危害.

效果展示:
  先打下廣告: 網頁闖關游戲入口(請狠狠地點擊我, ^_^).
  本文的想法實施於第十一關--健忘的教授.
  
  很直接的呈現一個登陸對話框, 考驗玩家能否借助非常規的方式來繞開登陸驗證.

SQL注入攻擊:
  雖然SQL注入攻擊已是老生常談, 不過還是得嘮叨幾句"科普"一下, ^_^.
  應用程序再獲取到用戶提交的數據后, 有些會進行SQL語句的拼接並執行. 倘若用戶提交的數據中包含SQL執行命令, 同時程序並沒有對數據進行過濾和安全驗證. 這樣就有可能繞過驗證並獲取數據, 甚至注入惡意代碼, 導致數據被篡改, 丟失.
  • 以用戶登錄為例
  服務的用戶數據模型如下:

table tb_user (
  username varchar(32),
  password varchar(32)
);

  服務的登陸驗證SQL如下:

SELECT * FROM tb_user WHERE username = '?' AND password = '?';

  登陸的輸入框入圖所示:
  
  其對應的form表單為:

<form method="post">
  用戶名: <input name="username" />
  密碼: <input name="password" type="password"/>
</form>

  hacker只要在form表單中, 巧妙設計字段內容, 注入sql執行命令, 以繞過數據驗證.
  比如username字段, 填寫為: ' or 1 = 1 #.
  這樣服務器端, 最終拼接的SQL為:

SELECT * FROM tb_user WHERE username = '' or 1 = 1 #' AND password= '?'

  由於字符'#'在SQL規范中, 表示注釋, '#'字符后直到行尾的所有字符都將被忽略掉.
  因此最終的SQL等價於如下:

SELECT * FROM tb_user WHERE username = '' or 1 = 1

  用戶數據將被返回, 如果嘗試登錄的是管理員后台, 那hacker將輕松獲取到管理員的權限, 這非常的可怕.
  魔高一丈道高一尺, 既然知道SQL注入的攻擊原理是什么? 那么防范措施就有針對性了, 千萬不要相信用戶提交的數據, 做好過濾和驗證.

關卡設計:
  本關就是來考察玩家對SQL注入的認知功底. 因此模擬構建了一個登陸窗口, 接受開放式的答案.
  如何對答案進行驗證呢? 1). 模擬SQL的執行解析. 2). 直接跑真實的SQL.
  對於方案一, 工作量大, 多個SQL命令需要支持, 有可能覆蓋不全所有的解, 有點得不償失.
  對於方案二, 容易實現, 但是給系統帶來了潛在風險, 比如注入drop tables等危險的命令.
  權衡比較, 還是采用第二種方案, 至於風險控制亦可控.
  服務程序是采用Java來編寫的, 若要放開SQL注入漏洞限定, 那就不能使用mybatis/hibernate這些ORM框架, 因為這些框架已經幫我們做了escape數據的工作.
  讓我們回到石器時代, 直接裸用jdbc來實現, 代碼如下:

/**
*
* 構造經典的SQL注入攻擊
* @param username 用戶輸入的用戶名
* @param password 用戶輸入的密碼
* @return
*/
public boolean verifySQLInject(String username, String password) {

  Connection connection = null;

  try {
    // *) 動態載入Mysql Driver驅動類
    Class.forName("com.mysql.jdbc.Driver");

    // *) 獲取 DB Connection
    connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);

    Statement stmt = connection.createStatement();

    String sql = String.format(
              "SELECT * " +
              "FROM tb_virtual_user " +
              "WHERE username = '%s' AND password = '%s'",
              username, password
      );
    ResultSet rs = stmt.executeQuery(sql);
    if ( rs.next() ) {
      stmt.close();

      // *) 登陸成功
      return true;
    }
  } catch (ClassNotFoundException e) {
    e.printStackTrace();
  } catch (SQLException e) {
    e.printStackTrace();
  } finally {
    if ( connection != null ) {
      try {
        connection.close();
      } catch (SQLException e) {
        // e.printStackTrace();
      }
    }
  }
  return false;

}

  注: 該代碼確切地執行了用戶登錄的SQL語句, 常規登錄和非法sql注入構造都將返回失敗.
  同時正如前文所提到的, 為了驗證SQL注入攻擊, 從而放棄數據驗證和過濾. 萬一有人不是用於解題, 而是專門搞破壞怎么辦? 猶如自己給自己埋了個炸彈, 你永遠不知道它什么時候爆炸.
  事實上, 這是多慮的. 我們可以創建兩個mysql賬號, 一個專門用於sql注入驗證(只授予select權限), 而剩下則用於其他的業務數據. 這樣就輕松做到隔離, 且十分安全.

GRANT USAGE ON *.* TO 'game1001'@'localhost' IDENTIFIED BY PASSWORD '*25A2CD7698FEED80089150F089755D752423A821';
GRANT SELECT ON `db_gameweb`.`tb_virtual_user` TO 'game1001'@'localhost';

  比如創建賬號game1001, 它只被授予對tb_virtual_user表的只讀權限.
  這樣服務就能允許sql注入存在, 但這些sql注入不具攻擊性.

后記:
  其實對該題, 我還是很滿意的. 我一直希望能構建類似的題, 寓教於樂. 就當自己一個以后奮斗的方向吧! 與君共勉.

公眾號&游戲站點:
  個人微信公眾號: 木目的H5游戲世界
  
  個人游戲作品集站點(尚在建設中...): www.mmxfgame.com,  也可直接ip訪問http://120.26.221.54/.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM