安全漏洞問題6:SQL隱碼攻擊

inzaghi1984發表於2017-11-21

安全漏洞問題6:SQL隱碼攻擊
1.1. 漏洞描述
SQL隱碼攻擊是指利用現有應用程式,將(惡意)的SQL命令注入到後臺資料庫引擎執行的攻擊方法。
通常Web 應用程式在後端使用資料庫,查詢資料庫的語言一般是 SQL(各大資料庫供應商都有自己的不同版本)。Web 應用程式通常會獲取使用者輸入(取自 HTTP 請求),將它併入 SQL 查詢中,然後傳送到後端資料庫。接著應用程式便處理查詢結果,有時會向使用者顯示結果。
如果應用程式對使用者(攻擊者)的輸入處理不夠安全,攻擊者便可以利用SQL發起攻擊。在此情況下,攻擊者可以注入惡意的資料,當該資料併入 SQL 查詢中時,將修改原來的查詢語句使攻擊者能進行敏感資料查詢等惡意操作。
1.2. 漏洞危害
惡意攻擊者可以利用實施SQL隱碼攻擊造成以下危害:
 檢視、修改或刪除資料庫條目和表
 篡改網頁內容,網頁掛馬
 查詢使用者的機密資訊,包括賬戶,個人私密資訊,交易資訊等等
 訪問資料庫系統表,查詢資料庫及承載伺服器敏感資訊
 獲取資料庫訪問許可權,甚至獲得DBA許可權
 執行各種作業系統命令,獲取承載主機作業系統和網路的訪問許可權
1.3. 解決方案
針對SQL隱碼攻擊的特點,可以從以下幾個方面進行防範:
 使用引數化方式進行查詢
應儘可能避免使用拼接的動態SQL語句,而使用引數化方式進行查詢。一段示例程式碼如下所示:

String user = request.getParameter(“username”);

String pass = request.getParameter("password");
String query = "SELECT id FROM users WHERE username=? AND password=?";
PreparedStatement stmt = con.prepareStatement(query);
stmt.setString(1, user);
stmt.setString(2, pass);
ResultSet rs = stmt.executeQuery();

if (rs.next())

{
// 登入成功
int id = rs.getInt(1);
...
}
else
{
// 登入失敗
...
}

 檢查使用者輸入的有效性
必須在服務端驗證使用者輸入的值和型別是否符合程式預期,一般需要驗證以下內容:
 必需欄位
必需欄位:檢查欄位不為空,並且其長度要大於零,不包括行距和後面的空格,一段示例程式碼如下所示:
// Java 示例-必需欄位檢查
public Class Validator {

  ...
  public static boolean validateRequired(String value) {
      boolean isFieldValid = false;
      if (value != null && value.trim().length() > 0) {
          isFieldValid = true;
      }
      return isFieldValid;
  }
  ...

}

String fieldValue = request.getParameter(“fieldName”);
if (Validator.validateRequired(fieldValue)) {

  // fieldValue is valid, continue processing request
  ...

}

 欄位資料型別
欄位資料型別檢查:檢查輸入欄位型別是否符合預期,比如說所有 HTTP 請求引數或 cookie 值的型別都是“字串”。
// Java 示例-驗證數字欄位型別
public Class Validator {

  ...
  public static boolean validateInt(String value) {
      boolean isFieldValid = false;
      try {
          Integer.parseInt(value);
          isFieldValid = true;
      } catch (Exception e) {
          isFieldValid = false;
      }
      return isFieldValid;
  }
  ...

}
// Java 示例-將HTTP 請求引數轉換為其各自的資料型別

String fieldValue = request.getParameter(“fieldName”);
if (Validator.validateInt(fieldValue)) {

  // convert fieldValue to an Integer
  Integer integerValue = Integer.getInteger(fieldValue);
  // store integerValue in a request attribute
  request.setAttribute("fieldName", integerValue);

}

// Use the request attribute for further processing
Integer integerValue = (Integer)request.getAttribute(“fieldName”);

 欄位長度
欄位長度檢查:檢查輸入欄位長度是否符合預期。
// Java 示例-userName 欄位的長度是否在 8 至 20 個字元之間
public Class Validator {

  ...
  public static boolean validateRange(int value, int min, int max) {
      return (value >= min && value <= max);
  }
  ...

}

String fieldValue = request.getParameter(“numberOfChoices”);
if (Validator.validateRequired(fieldValue)) {

  if (Validator.validateInt(fieldValue)) {
      int numberOfChoices = Integer.parseInt(fieldValue);
      if (Validator.validateRange(numberOfChoices, 10, 20)) {
          // numberOfChoices is valid, continue processing request
          ...
      }
  }

}
 欄位選項
欄位選項檢查:根據功能需求定義的受允許選項來驗證選定的使用者值。
// 欄位選項示例
public Class Validator {

  ...
  public static boolean validateOption(Object[] options, Object value) {
      boolean isValidValue = false;
      try {
          List list = Arrays.asList(options);
          if (list != null) {
              isValidValue = list.contains(value);
          }
      } catch (Exception e) {
      }
      return isValidValue;
  }
  ...

}

// Allowed options
String[] options = {“option1”, “option2”, “option3”);
// Verify that the user selection is one of the allowed options
String userSelection = request.getParameter(“userSelection”);
if (Validator.validateOption(options, userSelection)) {

  // valid user selection, continue processing request
  ...

}
 欄位模式
欄位模式檢查:檢查使用者輸入與由功能需求定義的模式是否匹配。例如,使用以下正規表示式:^[a-zA-Z0-9]*$驗證 userName 欄位是否符合“僅允許字母數字字元,且不區分大小寫,”
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
public Class Validator {

  ...
  public static boolean matchPattern(String value, String expression) {
      boolean match = false;
      if (validateRequired(expression)) {
           RE r = new RE(expression);
           match = r.match(value);             
      }
      return match;
  }
  ...

}

// Verify that the userName request parameter is alpha-numeric
String userName = request.getParameter(“userName”);
if (Validator.matchPattern(userName, “^[a-zA-Z0-9]*$”)) {

  // userName is valid, continue processing request
  ...

}
 對使用者輸入進行過濾
在所有資料進入應用程式之前把可能的危險攔截。這些處理在伺服器端完成。針對關鍵字和非法的HTML 程式碼等,可以編寫函式對其進行檢查或過濾。需要檢查或過濾的特殊字元至少包含如下表字元:
特殊字元(不區分大小寫) 關鍵字(不區分大小寫)
|(豎線符號) and
& (& 符號) exec
;(分號) insert
$(美元符號) select
%(百分比符號) delete
@(at 符號) update
`(單引號) count
“(引號) chr
`(反斜槓轉義單引號) mid
“(反斜槓轉義引號) master
<>(尖括號) truncate
()(括號) char
+(加號) declare
CR(回車符,ASCII 0x0d) backup
LF(換行,ASCII 0x0a) script
,(逗號)
(反斜槓)
*(星號)
一段檢查關鍵字的示例程式碼如下:
dim sql_injdata
SQL_injdata = “`|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|–|”
SQL_inj = split(SQL_Injdata,”|”)
If Request.QueryString<>”” Then
 For Each SQL_Get In Request.QueryString
  For SQL_Data=0 To Ubound(SQL_inj)
   if instr(Request.QueryString(SQL_Get),Sql_Inj(Sql_DATA))>0 Then
    Response.Write “<Script Language=**>alert(`引數中包含非法字元嘗試注入!`);history.back(-1)</Script>”
    Response.end
   end if
  next
 Next
End If

If Request.Form<>”” Then
 For Each Sql_Post In Request.Form
  For SQL_Data=0 To Ubound(SQL_inj)
   if instr(Request.Form(Sql_Post),Sql_Inj(Sql_DATA))>0 Then
    Response.Write “<Script Language=**>alert(`引數中包含非法字元嘗試注入! `);history.back(-1)</Script>”
    Response.end
   end if
  next
 next
 加固應用程式伺服器和資料庫,利用最低許可權賬戶與資料庫連線。
 配置可信任的IP接入和訪問(例如,IPSEC),以控制哪些機器能夠與資料庫伺服器通訊。
 從資料庫伺服器上移除所有的示例指令碼和應用程式。
 不要使用sa、dba、admin等具備資料庫DBA許可權的賬戶,為每一個應用程式的資料庫連線賬戶使用一個專用的最低許可權權賬戶。如果應用程式僅需要讀取訪問,就要將資料庫的訪問限制為只讀。
 從生產資料庫中移除未用的儲存過程。
 將對應用程式的訪問僅授權給使用者建立的儲存過程。
 禁止應用程式訪問不必要的系統儲存過程。
 應用程式儘量使用儲存過程,利用儲存過程,將資料訪問抽象化,讓使用者不直接訪問表或檢視。


相關文章