滲透測試概念:
詳見百度百科
http://baike.baidu.com/link?url=T3avJhH3_MunEIk9fPzEX5hcSv2IqQlhAfokBzAG4M1CztQrSbwsRkSerdBe17H6tTF5IleOCc7R3ThIBYNO-q
前言:
安全測試范圍極廣,開門見山,樓主對這行了解的也不是太深,也是在學習探索階段,此文,也是對自己學習的總結與記錄和簡單的分享;這里沒有具體工具的使用方法,更多的是原理細節的了解和解決方案的探討。
code部分:
html+jsp+mysql,實現登錄與新增數據功能。
html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>web安全測試之sql注入</title> </head> <body bgcolor="#ffffff"> <form action="chkLogin.jsp" method="POST"> <input type="text" name="user" /><br /> <input type="password" name="pass" /><br /> <input type="Submit" value="登錄" /> </form> <form action="insert.jsp" method="POST"> <input type="text" name="user" /><br /> <input type="text" name="pwd" /><br /> <input type="Submit" value="新增" /> </form> </body> </html>
jsp1

<%@ page contentType="text/html; charset=utf-8" %> <%@ page import="java.sql.*" %> <html> <head> <title>chkLogin.jsp</title> </head> <body bgcolor="#ffffff"> <% String user = request.getParameter("user"); String pass = request.getParameter("pass"); Connection con = null; PreparedStatement ps = null; ResultSet rs = null; String sql =null; try { Class.forName("com.mysql.jdbc.Driver"); con = java.sql.DriverManager.getConnection("jdbc:mysql://192.168.0.157/webTest?useUnicode=true&characterEncoding=utf-8","root","123456"); sql= "SELECT * FROM user WHERE name='"+user+"' AND pwd ='"+pass+"'"; ps = con.prepareStatement(sql); // ps = con.prepareStatement("SELECT * FROM user WHERE name=? AND pwd =?"); // ps.setString(1, user); // ps.setString(2, pass); rs = ps.executeQuery(); if ( rs.next()) { out.println("登錄成功!"); out.println("sql:"+sql); } else { out.println("登錄失敗!"); out.println("sql:"+sql); } }catch(Exception ex) { out.println("登錄異常!"); out.println("sql:"+sql); out.println("Exception:"+ex); }finally{ if ( null != rs) { rs.close(); rs = null; } if ( null != ps) { ps.close(); ps = null; } if ( null != con){ con.close(); con = null; } } %> </body> </html>
jsp2

<%@ page contentType="text/html; charset=utf-8" %> <%@ page import="java.sql.*" %> <html> <head> <title>chkLogin.jsp</title> </head> <body bgcolor="#ffffff"> <% String user = request.getParameter("user"); String pass = request.getParameter("pwd"); Connection con = null; PreparedStatement ps = null; ResultSet rs = null; String sql =null; int r = 0; try { Class.forName("com.mysql.jdbc.Driver"); con = java.sql.DriverManager.getConnection("jdbc:mysql://192.168.0.157/webTest?useUnicode=true&characterEncoding=utf-8","root","123456"); sql= "insert into user(name,pwd) values ('"+user+"','"+pass+"')"; ps = con.prepareStatement(sql); // ps = con.prepareStatement("SELECT * FROM user WHERE name=? AND pwd =?"); // ps.setString(1, user); // ps.setString(2, pass); r = ps.executeUpdate(); if (r>0) { out.println("添加成功!"); out.println("sql:"+sql); } else { out.println("添加失敗!"); out.println("sql:"+sql); } }catch(Exception ex) { out.println("添加異常!"); out.println("sql:"+sql); out.println("Exception:"+ex); }finally{ if ( null != rs) { rs.close(); rs = null; } if ( null != ps) { ps.close(); ps = null; } if ( null != con){ con.close(); con = null; } } %> </body> </html>
登錄原理簡介:
html->jsp->db
html頁面輸入兩個參數user、pass,按‘登錄’按鈕,調用chkLogin.jsp;chkLogin.jsp接收html傳入兩個參數,去數據庫user表里面查詢,返回不為null,則登錄成功,否則登錄失敗,異常則登錄異常。查詢使用的方法是executeQuery,sql組裝使用的+參數拼接。
正常場景:
數據庫用戶數據
頁面登錄(密碼錯誤)
頁面登錄正常
到這里為止,我們的實驗的環境有了,現在可以大展手腳了~~開始~
sql注入實例1(不知道用戶密碼密碼情況下,登錄):
界面信息輸入值
user:test
pwd: ' or 1=1; --
效果
sql注入實例2(不知道用戶名與密碼情況下,登錄):
界面信息輸入值:
user:' or 1=1 ; --
pwd:
效果:
上面兩個實例攻防升級案例:
初級方案:界面前端控制--界面參數做過濾與限制;比如' -- ;字符,or字符等;
應對方案:通過fiddler等http協議抓包工具,用戶名與密碼可以自由編輯,注意瀏覽器做了url編碼,直接請求繞過前端字符串控制。
實例的升級方案:
中級方案:前端控制+邏輯業務控制,邏輯業務控制舍棄使用+拼接方方式,采取獲取參數方式實現:
ps = con.prepareStatement("SELECT * FROM user WHERE name=? AND pwd =?");
ps.setString(1, user);
ps.setString(2, pass);
應對方案:
界面字符串輸入參數注入與http協議接口方式參數注入失效。
嘗試方案(未實踐):
對參數進行各種編碼轉義,這個環節的內容比較多,樓主水平有限,這塊有興趣的歡迎補充。
以上,就是一個簡單的實例,從以上列子中,也沒見到多大的危險性啊,只是進入系統而已~~也沒見到能產生多大的危險性與數據泄露的重大風險漏洞啊啊~~好的,大菜開始上場~~
新增數據功能原理:
參考登錄~
新增功能正常使用:
sql注入實例3(任意添加數據):
界面輸入數據
test','test'),('1','2'); --
效果:
好戲從這里開始~~
第一步,獲取當前數據庫版本,SELECT version()的使用~
sql注入實例4:
界面輸入信息
test',(SELECT version())) --
效果:
第二步,獲取數據庫數據庫對象,information_schema.TABLES使用~
sql注入實例5:
界面輸入參數:
test',(select table_schema from information_schema.TABLES group by table_schema limit 1)); --
效果:
dblist
界面:
數據:
后面的過程就是周而復始,你懂的~直到獲取所有db
第三步,獲取db庫下面的表對象與表結構
方法類似,對 information_schema.TABLES熟悉
第四步,獲取db用戶名信息
哈哈,方式類似,對information_schema.user熟悉
第五步,重置用戶密碼
哈哈,方式類似,對information_schema熟悉
第六步,獲取ip,這個很多方式
上面都得到了,差不多可以宣告GG了~~
解決方案&探討:
從兩個維度來分析,第一個應用層角度,從前端到業務層再到db層。
第二個維度,從軟件七層架構角度來,應用層->傳輸層->網絡層->數據鏈路層->物理層.
具體如下
1.前端對參數嚴格控制;
2.業務層不要使用拼接字符串實現方式;
3.業務功能請求,增加token字段控制,每次post請求對koken進行有效驗證;
4.傳輸協議,涉及到數據接口參數安全,采取https協議傳輸;
5.數據庫,采取最小原子控制,對用戶,用戶權限進行嚴格的權限控制,能做數據讀取與數據插入的業務能單獨分別使用不同用戶盡量區分;
6.應用層訪問db,對數據庫配置相關信息,特別是pwd字段進行特定算法加密;
7.數據庫與應用程序部署在內網環境,與外網進行隔離;
8.系統方面,歡迎運維童鞋補充;
9.其它維度歡迎補充與討論。
現碼的,下班~周末快樂~