前言:
之前編寫了一個網頁闖關游戲(類似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/.