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

 

前言:
  以前編寫了一個網頁闖關遊戲(相似Riddle Game), 除了但願你們可以體驗一下個人遊戲外. 也願意分享編寫這個網頁遊戲過程當中, 學到的一些知識.
  web開發初學者每每會忽視一些常見的漏洞, 好比SQL注入攻擊, XSS攻擊. 本文將簡述SQL注入攻擊的原理, 並分享下關卡設計, 其在打開潘多拉魔盒的狀況下, 又能很好地限制危害.html

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

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

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

  服務的登錄驗證SQL以下:web

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

  登錄的輸入框入圖所示:
  
  其對應的form表單爲:sql

<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來實現, 代碼以下:mybatis

/**
*
* 構造經典的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/.

相關文章
相關標籤/搜索