首先給大家看個例子: 1)小編首先在資料庫中建立了一張測試表logintable,表內有一條測試信息: 然後寫了個測試程式: 輸出結果為: 2)然後我們修改main()方法中,login()方法調用時的參數,改為: 執行結果: 3)似乎一切都天經地義,沒什麼問題。但是這時我們再對login()方法調 ...
首先給大家看個例子:
1)小編首先在資料庫中建立了一張測試表logintable,表內有一條測試信息:
然後寫了個測試程式:
package com.java.SqlInject; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class SqlInject { private static String Driver="com.mysql.jdbc.Driver"; //資料庫驅動 //連接資料庫的URL地址 private static String url="jdbc:mysql://localhost:3306/hellojdbc?useUnicode=true&characterEncoding=UTF-8"; private static String username="root";//資料庫連接用戶名 private static String password="123456";//資料庫連接密碼 private static Connection conn=null;//資料庫連接對象 private static Statement stat=null;//語句陳述對象 private static ResultSet rs=null;//結果數據集 private static PreparedStatement pst=null;//預編譯語句 //使用靜態塊的方式載入驅動 static { try { //調用Class對象的靜態forName()方法載入資料庫驅動類 Class.forName(Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //使用單例模式返回資料庫連接對象 public static Connection getConnection() throws SQLException{ if(conn==null){ conn=DriverManager.getConnection(url, username, password); return conn; } return conn; } public static void login(String name,String password){ try { conn=getConnection(); stat=conn.createStatement(); //使用動態拼接的方式拼接sql語句 rs=stat.executeQuery("select * from logintable where name='"+name+"' and password='"+password+"'"); if(rs.next()){ System.out.println("用戶已註冊"); }else System.out.println("無記錄"); } catch (SQLException e) { e.printStackTrace(); } } public static void main(String[] args) { login("zhangsan","123456"); } }
輸出結果為:
2)然後我們修改main()方法中,login()方法調用時的參數,改為:
public static void main(String[] args) { login("zhangsan","123"); }
執行結果:
3)似乎一切都天經地義,沒什麼問題。但是這時我們再對login()方法調用的參數做一下修改:
public static void main(String[] args) { //註意:第一個參數兩個短杠後面有空格 login("zhangsan';-- ","123"); }
測試執行結果又變成了:
明明用戶名和密碼都不對,資料庫中也沒有這條記錄,為什麼會出現這種情況?
這就是SQL註入帶來的漏洞問題。
惡意用戶通過偽裝請求,來騙過我們的業務程式,達到獲取資料庫核心數據的目的。
通過上面的例子,我們可以看到我們最後一次改寫參數來調用login()方法的時候,java業務程式中接收到的不是我們期望的那個sql語句。
由於分號的存在,使得我們的sql語句拼接後完變成了這樣:
select * from logintable where name='zhangsan'; -- 'password='123';
這樣就由一條sql語句變成了兩條sql語句。在第一條的sql語句中去掉了密碼的檢索條件,同時註釋掉了第二條sql語句。
(兩個橫線為註釋符)
總結一下:
SQL註入就是用戶在輸入表單或者URL參數中輸入SQL命令,到達欺騙應用程式的目的,破壞原有SQL的語義,發送惡意的SQL到後端資料庫,導致資料庫信息出現泄露的漏洞。
發生這種漏洞的原因是:
我們的sql語句是通過動態拼接組成的,在拼接完成之前,sql語句是不完整的,所以當在拼接時新加入的參數中有sql命令的註入就有可能改變原有的sql語義。
比方像上面的例子中,密碼被惡意地屏蔽註釋掉了。
解決方法:
傳入外部參數時,不使用動態拼接的方式拼接sql語句;使用參數化方式的sql實現方式(格式化,占位符),即使用預編譯的statement。
然後傳參:
所以上面例子中相關代碼應修改為:
conn=getConnection(); //stat=conn.createStatement(); //使用組合的方式拼接sql語句 //rs=stat.executeQuery("select * from logintable where name='"+name+"' and password='"+password+"'"); pst=conn.prepareStatement("select * from logintable where name= ? and password= ?"); pst.setString(1, name); pst.setString(2, password); rs=pst.executeQuery(); if(rs.next()){ System.out.println("用戶已註冊"); }else System.out.println("無記錄");
其他註意事項:
使用嚴格的資料庫管理許可權:
1.僅給予Web應用訪問資料庫的最小許可權;
2.避免Drop table等許可權。
封裝資料庫錯誤:
1.禁止直接將後端資料庫異常信息暴露給用戶;
2.對後端異常信息進行必要的封裝,避免用戶直接查看到後端異常。
機密信息禁止明文存儲:
1.涉密信息需要加密處理;
2.使用AES_ENCRYPT/AES_DECRYPT加密和解密。