PS:今天偶然間發現了SQL的注入...所以就簡單的腦補了一下,都是一些簡單的例子...這篇寫的不怎么樣...由於自己沒有進行很深的研究...
學習內容:
1.SQL注入的概念...
所謂SQL注入,就是通過把SQL命令插入到Web表單遞交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令,比如先前的很多影視網站泄露VIP會員密碼大多就是通過WEB表單遞交查詢字符暴出的,這類表單特別容易受到SQL注入式攻擊.當應用程序使用輸入內容來構造動態sql語句以訪問數據庫時,會發生sql注入攻擊。如果代碼使用存儲過程,而這些存儲過程作為包含未篩選的用戶輸入的字符串來傳遞,也會發生sql注入。 黑客通過SQL注入攻擊可以拿到網站數據庫的訪問權限,之后他們就可以拿到網站數據庫中所有的數據,惡意的黑客可以通過SQL注入功能篡改數據庫中的數據甚至會把數據庫中的數據毀壞掉。
2.SQL注入產生的原因...
sql注入攻擊是利用是指利用設計上的漏洞,在目標服務器上運行Sql語句以及進行其他方式的攻擊,動態生成Sql語句時沒有對用戶輸入的數據進行驗證是Sql注入攻擊得逞的主要原因。對於java數據庫連接JDBC而言,SQL注入攻擊只對Statement有效,對PreparedStatement是無效的,這是因為PreparedStatement不允許在不同的插入時間改變查詢的邏輯結構。
3.SQL注入原理...
SQL注射能使攻擊者繞過認證機制,完全控制遠程服務器上的數據庫。 SQL是結構化 查詢語言的簡稱,它是訪問數據庫的事實標准。目前,大多數Web應用都使用SQL數據庫來存放應用程序的數據。幾乎所有的Web應用在后台 都使用某種 SQL數據庫。跟大多數語言一樣,SQL語法允許數據庫命令和用戶數據混雜在一起的。如果開發人員不細心的話,用戶數據就有可能被解釋成命令, 這樣的 話,遠程用戶就不僅能向Web應用輸入數據,而且還可以在數據庫上執行任意命令了。
SQL注入式攻擊的主要形式有兩種。一是直接將代碼插入到與SQL命令串聯 在一起並使得其以執行的用戶輸入變量。上面筆者舉的例子就是采用了這種方法。由於其直接與SQL語句捆綁,故也被稱為直接注入式攻擊法。二是一種間接的攻 擊方法,它將惡意代碼注入要在表中存儲或者作為原書據存儲的字符串。在存儲的字符串中會連接到一個動態的SQL命令中,以執行一些惡意的SQL代碼。注入 過程的工作方式是提前終止文本字符串,然后追加一個新的命令。如以直接注入式攻擊為例。就是在用戶輸入變量的時候,先用一個分號結束當前的語句。然后再插 入一個惡意SQL語句即可。由於插入的命令可能在執行前追加其他字符串,因此攻擊者常常用注釋標記“—”來終止注入的字符串。執行時,系統會認為此后語句 位注釋,故后續的文本將被忽略,不背編譯與執行。
4.舉例說明
這里我只是使用簡單的語句來實現SQL的注入,只是為了讓大家簡單的看一下注入后的效果...都是一些簡單的例子,一般黑客是不可能用我這種太簡單方式來實現注入的,一般都是很復雜的東西...由於自己也不打算過深的研究這個東西,所以就上一些簡單的例子..
package JDBC_4_SQL_injection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.SQLException; import java.sql.ResultSet; public class injection_2 { static final String DB_URL="jdbc:mysql://localhost/java_mysql"; static final String USER="root"; static final String PAS="49681888"; public static void main(String[] args) { // TODO Auto-generated method stub Connection cn =null; Statement pt=null; ResultSet rs=null; String sql="select id,name,age from test where name=''or 1 or''";//這里的or 1 or表示的是永真式...無論后面有多少語句,都會被忽略掉... try { Class.forName("com.mysql.jdbc.Driver"); cn=DriverManager.getConnection(DB_URL,USER,PAS); pt=cn.createStatement(); rs=pt.executeQuery(sql); //由於實現了SQL注入,因此我就能夠獲取,數據庫中所有的數據信息,這樣數據庫里的數據就造成了數據泄露... while(rs.next()) { System.out.println(rs.toString()); System.out.println(rs.getObject(2)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { cn.close(); pt.close(); rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
很簡單的實現了注入...這里使用了Statement來保存我們的因為sql語句中把1認為是true,又因為是或的關系,所以將所有的數據查詢出來了,這個就是sql注入,因為Statement會把傳遞進來的參數進行一下轉化操作,用引號包含一下,所以會出現這個問題,那么我們該怎么解決呢?有的 同學說我們可以添加一句過濾的代碼,將傳遞的參數取出單引號,這個方法是可行的的,但是這個只能解決那些使用單引號的數據庫,可能有的數據庫使用的是雙引 號包含內容,那就不行了,所以應該想一個全套的方法,那么這里我們就是用一個叫做:PreparedStatement類,這個類是Statement類 的子類..可以防止這種情況的發生...
package JDBC_4_SQL_injection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.ResultSet; public class injection_2 { static final String DB_URL="jdbc:mysql://localhost/java_mysql"; static final String USER="root"; static final String PAS="49681888"; public static void main(String[] args) { // TODO Auto-generated method stub Connection cn =null; PreparedStatement pt=null; ResultSet rs=null; String sql="select id,name,age from test where name=?"; try { Class.forName("com.mysql.jdbc.Driver"); cn=DriverManager.getConnection(DB_URL,USER,PAS); pt=cn.prepareStatement(sql); pt.setString(1,"'or 1 or'");//在這里進行傳遞參數... rs=pt.executeQuery(); while(rs.next()) { System.out.println(rs.toString()); System.out.println(rs.getObject(2)); }//這里就不會有查詢結果了... } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { cn.close(); pt.close(); rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
正如上文所描述的,SQL 漏洞危害非常的巨大,但我相信國內很多中小站點還普遍存在着這樣的漏洞。因此建議:
1、代碼要對輸入的參數做到充分的過濾,並盡可能得考慮極端情況
2、錯誤信息盡可能的少,否則無關的人看不懂而有心的人就會提起興趣
3、不要以管理員的身份運行服務器進程
4、某些情況下,net 命令對於攻擊者而言就是“微軟牌”的木馬
5、嚴格控制遠程登錄訪問者的來源
6、如果可能的情況下,不是很推薦使用 Windows 作為服務器操作系統
/*另一個注入例子... *使用幾個查詢語句... * */ package JDBC_4_SQL_injection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.Statement; import java.sql.*; public class injection_1 { static final String DB_URL="jdbc:mysql://localhost:3306/java_mysql?allowMultiQueries=true";//?allowMultiQueries=true這句話的目的是允許使用多個sql語句進行對數據庫的操作... static final String USER="root"; static final String PAS="49681888"; public static void main(String[] args) { // TODO Auto-generated method stub String name="clearlove';delete from test;select * from test where name='clearlove"; String sql=createSQL(name); System.out.println(sql); Connection cn=null; Statement st=null; try { Class.forName("com.mysql.jdbc.Driver"); cn=DriverManager.getConnection(DB_URL,USER,PAS); st=cn.createStatement();//實現了注入...這樣我們的數據庫信息將全部刪除掉... st.execute(sql); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { cn.close(); st.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private static String createSQL(String name){ String sql="select id, name, age from test "; if(name!=null && name.length()!=0){ sql+= "where name='"+name+"'"; } return sql; } }
上述代碼完成了注入,直接將數據庫里面的數據進行了刪除...
/* * */ package JDBC_4_SQL_injection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.*; public class injection_1 { static final String DB_URL="jdbc:mysql://localhost:3306/java_mysql?allowMultiQueries=true"; static final String USER="root"; static final String PAS="49681888"; public static void main(String[] args) { // TODO Auto-generated method stub String sql="select id,name,age from test where name=?"; Connection cn=null; PreparedStatement st=null; try { Class.forName("com.mysql.jdbc.Driver"); cn=DriverManager.getConnection(DB_URL,USER,PAS); st=cn.prepareStatement(sql); st.setString(1, "clearlove';delete from test;select * from test where name='clearlove"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { cn.close(); st.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private static String createSQL(String name){ String sql="select id, name, age from test "; if(name!=null && name.length()!=0){ sql+= "where name='"+name+"'"; } return sql; } }
這里也使用了PreparedStatement來解決了這個問題的發生....
雖然使用PreparedStatement可以避免sql的注入,但是並不意味着我們在任何時候都使用PreparedStatement,有很多時候也必須要使用Statement...這個要具體情況具體分析...
小結
