1 SQL注入、盲注
1.1 SQL注入、盲注概述
Web 應用程序通常在后端使用數據庫,以與企業數據倉庫交互。查詢數據庫事實上的標准語言是 SQL(各大數據庫供應商都有自己的不同版本)。Web 應用程序通常會獲取用戶輸入(取自 HTTP 請求),將它並入 SQL 查詢中,然后發送到后端數據庫。接着應用程序便處理查詢結果,有時會向用戶顯示結果。 如果應用程序對用戶(攻擊者)的輸入處理不夠小心,攻擊者便可以利用這種操作方式。在此情況下,攻擊者可以注入惡意的數據,當該數據並入 SQL 查詢中時,就將查詢的原始語法更改得面目全非。例如,如果應用程序使用用戶的輸入(如用戶名和密碼)來查詢用戶帳戶的數據庫表,以認證用戶,而攻擊者能夠將惡意數據注入查詢的用戶名部分(或密碼部分),查詢便可能更改成完全不同的數據復制查詢,可能是修改數據庫的查詢,或在數據庫服務器上運行 Shell 命令的查詢。
1.2 安全風險及原因
高風險漏洞,攻擊者可能會查看、修改或刪除數據庫條目和表
原因:未對用戶輸入正確執行危險字符清理
固定值: 查看危險字符注入的可能解決方案
1.3 AppScan掃描建議
若干問題的補救方法在於對用戶輸入進行清理。 通過驗證用戶輸入未包含危險字符,便可能防止惡意的用戶導致應用程序執行計划外的任務,例如:啟動任意 SQL 查詢、嵌入將在客戶端執行的 Javascript 代碼、運行各種操作系統命令,等等。 建議過濾出所有以下字符:
[1] |(豎線符號)
[2] & (& 符號)
[3];(分號)
[4] $(美元符號)
[5] %(百分比符號)
[6] @(at 符號)
[7] '(單引號)
[8] "(引號)
[9] \'(反斜杠轉義單引號)
[10] \"(反斜杠轉義引號)
[11] <>(尖括號)
[12] ()(括號)
[13] +(加號)
[14] CR(回車符,ASCII 0x0d)
[15] LF(換行,ASCII 0x0a)
[16] ,(逗號)
[17] \(反斜杠)
以下部分描述各種問題、問題的修訂建議以及可能觸發這些問題的危險字符:
SQL 注入和 SQL 盲注: A. 確保用戶輸入的值和類型(如 Integer、Date 等)有效,且符合應用程序預期。 B. 利用存儲過程,將數據訪問抽象化,讓用戶不直接訪問表或視圖。當使用存儲過程時,請利用 ADO 命令對象來實施它們,以強化變量類型。 C. 清理輸入以排除上下文更改符號,例如:
[1] '(單引號)
[2] "(引號)
[3] \'(反斜線轉義單引號)
[4] \"(反斜杠轉義引號)
[5] )(結束括號)
[6] ;(分號)
跨站點腳本編制: A. 清理用戶輸入,並過濾出 JavaScript 代碼。我們建議您過濾下列字符:
[1] <>(尖括號)
[2] "(引號)
[3] '(單引號)
[4] %(百分比符號)
[5] ;(分號)
[6] ()(括號)
[7] &(& 符號)
[8] +(加號)
LDAP 注入: A. 使用正面驗證。字母數字過濾(A..Z,a..z,0..9)適合大部分 LDAP 查詢。 B. 應該過濾出或進行轉義的特殊 LDAP 字符:
[1] 在字符串開頭的空格或“#”字符
[2] 在字符串結尾的空格字符
[3] ,(逗號)
[4] +(加號)
[5] "(引號)
[6] \(反斜杠)
[7] <>(尖括號)
[8] ;(分號)
[9] ()(括號)
ORM 注入: A. 確保用戶輸入的值和類型(如 Integer、Date 等)有效,且符合應用程序預期。 B. 利用存儲過程,將數據訪問抽象化,讓用戶不直接訪問表或視圖。 C. 使用參數化查詢 API D. 清理輸入以排除上下文更改符號,例如: (*):
[1] '(單引號)
[2] "(引號)
[3] \'(反斜線轉義單引號)
[4] \"(反斜杠轉義引號)
[5] )(結束括號)
[6] ;(分號)
1.4 應用程序解決方案
1、我們為了調試方便,在頁面上會拋出數據庫異常信息,如果入侵工具獲取了這些信息,就可以獲取系統的一些配置信息,如web系統框架、采用的數據庫等,從而找出系統漏洞。所以不要在頁面上拋出異常的詳細信息,這些信息對客戶並沒有用,只是方便技術人員調試罷了,處理方法是在異常處理頁面把打印異常代碼刪除即可;
2、新建一個過濾器,通過過濾器過濾SQL注入特殊字符,配置成功后,重啟服務,用Appsan工具掃描,漏洞得到解決。
通過過濾器可以解決SQL注入、跨站點腳本編制及通過框架釣魚等問題,具體實現方式如下:
1、在web.xml文件中配置過濾器
<!--配置過濾器 通過過濾器過濾SQL注入特殊字符 --> <filter> <filter-name>InjectFilter</filter-name> <filter-class>com.filter.InjectFilter</filter-class> </filter> <!--映射過濾器--> <!--<filter-mapping> <filter-name>InjectFilter</filter-name>--> <!--“/*”表示攔截所有的請求 --> <!-- <url-pattern>/*</url-pattern> </filter-mapping>--> <filter-mapping> <filter-name>InjectFilter</filter-name> <!--“/*”表示攔截所有的請求 --> <url-pattern>/*</url-pattern> </filter-mapping>
2、過濾器過濾代碼
package com.filter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.Filter;
import javax.servlet.http.HttpServlet;
/**
* 通過過濾器過濾SQL注入特殊字符
* */
public class InjectFilter extends HttpServlet implements Filter {
/**
*
*/
private static final long serialVersionUID = 5286703103846683570L;
private String failPage = "/info.jsp";//發生注入時,跳轉頁面
public void doFilter(ServletRequest request,ServletResponse response,
FilterChain filterchain)throws IOException, ServletException {
//判斷是否有注入攻擊字符
HttpServletRequest req = (HttpServletRequest) request;
String inj = injectInput(req);
if (!inj.equals("")) {
request.getRequestDispatcher(failPage).forward(request, response);
return;
} else {
// 傳遞控制到下一個過濾器
filterchain.doFilter(request, response);
}
}
/**
* 判斷request中是否含有注入攻擊字符
* @param request
* @return
*/
public String injectInput(ServletRequest request) {
Enumeration e = request.getParameterNames();
String attributeName;
String attributeValues[];
String inj = "";
String injdb = "";
while (e.hasMoreElements()) {
attributeName = (String)e.nextElement();
//不對密碼信息進行過濾,一般密碼中可以包含特殊字符
if(attributeName.equals("userPassword")||attributeName.equals("confirmPassword")||attributeName.equals("PASSWORD")
||attributeName.equals("password")||attributeName.equals("PASSWORD2")||attributeName.equals("valiPassword")){
continue;
}
attributeValues = request.getParameterValues(attributeName);
for (int i = 0; i < attributeValues.length; i++) {
if(attributeValues[i]==null||attributeValues[i].equals(""))
continue;
inj = injectChar(attributeValues[i]);
if (!inj.equals(""))
{
return inj;
}
}
}
return inj;
}
/**
* 判斷字符串中是否含有注入攻擊字符
* @param str
* @return
*/
public String injectChar(String str) {
String inj_str = "\" ) \' * % < > &";
String inj_stra[] = inj_str.split(" ");
for (int i = 0 ; i < inj_stra.length ; i++ )
{
if (str.indexOf(inj_stra[i])>=0)
{
return inj_stra[i];
}
}
return "";
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
// System.out.println("----注入過濾器初始化----");
}
}
3.如果跨站點的腳本編制還存在或者存在其他漏洞風險就再搞個過濾器繼續加固驗證過濾
1.在xml文件繼續配置一個新的過濾器:
<!--配置過濾器 跨站點腳本編制過濾 --> <filter> <filter-name>XssFilter</filter-name> <filter-class>com.filter.XssFilter</filter-class> </filter> <!--映射過濾器--> <filter-mapping> <filter-name>XssFilter</filter-name> <!--“/*”表示攔截所有的請求 --> <url-pattern>/*</url-pattern> </filter-mapping>
2.過濾器代碼:
package com.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import com.RequestWrapper;
import javax.servlet.http.HttpServlet;
/*
* XSS過濾
*/
public class XssFilter extends HttpServlet implements Filter {
/**
*
*/
private static final long serialVersionUID = 6846384055249714181L;
/**
* 需要排除的頁面
*/
private String excludedPages;
private String[] excludedPageArray;
@SuppressWarnings("unused")
private FilterConfig filterConfig;
public void destroy() {
this.filterConfig = null;
// System.out.println("----過濾器銷毀----");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean isExcludedPage = false;
// 強制類型轉換 HttpServletRequest
HttpServletRequest request2 = (HttpServletRequest) request;
String ctx_path = request2.getContextPath();
String request_url = request2.getRequestURI();
String action = request_url.substring(ctx_path.length());
HttpServletResponse response2 = (HttpServletResponse)response;
response2.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly");
if (isExcludedPage) {
chain.doFilter(request, response2);
} else { // 構造RequestWrapper對象處理XSS
chain.doFilter(new RequestWrapper(request2), response2);
}
}
/**
* 自定義過濾規則
*/
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
excludedPages = filterConfig.getInitParameter("excludedPages");
if (StringUtils.isNotEmpty(excludedPages)) {
excludedPageArray = excludedPages.replaceAll("[\\s]", "").split(",");
//System.out.println("----過濾器初始化----");
}
}
}
2.類RequestWrapper
package com.servlet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.util.XssClean;
/*
* 增加XSS過濾
* 自定義的RequestWrapper中,對getParameter和getParameterValues進行重寫,從而達到對各個用戶輸入的form參數的值進行過濾
*/
public class RequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;
public RequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 獲取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 獲取最原始的request的靜態方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof RequestWrapper) {
return ((RequestWrapper) req).getOrgRequest();
}
return req;
}
/**
* 覆蓋getParameterMap 方法,對sql、html、script注入進行過濾
*/
public Map<String,String> getParameterMap() {
HashMap<String,String> paramMap = (HashMap<String,String>) super.getParameterMap();
paramMap = (HashMap<String,String>) paramMap.clone();
for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String,String[]> entry = (Map.Entry<String,String[]>) iterator.next();
String [] values = entry.getValue();
for (int i = 0; i < values.length; i++) {
if(values[i] instanceof String){
values[i] = XssClean.xssClean(values[i]);
}
}
entry.setValue(values);
}
return paramMap;
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//濾掉form data中的惡意腳本
public String[] getParameterValues(String paramString){
String[] arrayOfString1 = super.getParameterValues(paramString);
if (arrayOfString1 == null)
return null;
int i = arrayOfString1.length;
String[] arrayOfString2 = new String[i];
for (int j = 0; j < i; j++)
arrayOfString2[j] =XssClean.xssClean_New(arrayOfString1[j]);
return arrayOfString2;
}
public String getParameter(String paramString){
String str = super.getParameter(paramString);
if (str == null)
return null;
return XssClean.xssClean(str);
}
//////////////////////////////////////////////////
//在輸入流中檢測濾掉來自其他網站的URL中的惡意腳本
public String getHeader(String paramString) {
String str = super.getHeader(paramString);
if (str == null)
return null;
return XssClean.xssClean(str);
}
////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
public String getQueryString() {
String value = super.getQueryString();
if (value != null) {
value = XssClean.xssClean(value);
}
return value;
}
}
3.類XssClean
package com.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.Policy;
import org.owasp.validator.html.PolicyException;
import org.owasp.validator.html.ScanException;
import org.springframework.web.util.HtmlUtils;
import org.apache.commons.io.IOUtils;
/*
* 增加 XSS過濾
* 加入了dependency之后,就可以在xssClean中加入antisamy對惡意腳本進行清理。其中policy.xml是白名單,
* policy.xml中規定了各個html元素所必須滿足的條件。antisamy的精髓之處在於,使用policy文件來規定你的過濾條件,
* 若輸入字符串不滿足policy文件中的條件,則會過濾掉字符中的惡意腳本,返回過濾后的結果。
*/
public class XssClean {
private static Policy policy = null;
public static Policy getPolicy() throws PolicyException {
if (policy == null) {
InputStream policyFile = XssClean.class.getResourceAsStream("/resource/antisamy.xml"); //antisamy.xml此文件可能大裝載會報錯,直接拋出異常即可不用理會也行。
// 推薦一個jar包,用來轉換InputStream到String,
// 引入apache的io包 import org.apache.commons.io.IOUtils;
String result = null;
try {
result = IOUtils.toString(policyFile, "UTF-8");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
policy = Policy.getInstance(result);
}
return policy;
}
//將client端用戶輸入的request,在server端進行了攔截,並且進行了過濾
此方法可以不用直接用下面的新的寫法即可
public static String xssClean_New(String value)
{
if (StringUtils.isNotEmpty(value)) {
AntiSamy antiSamy = new AntiSamy();
try {
//如果 不用此方法 可以 使用 下面注釋的代碼實現
final CleanResults cr = antiSamy.scan(StringUtil.UrlDecode(value, "UTF-8"), getPolicy());
// final CleanResults cr = antiSamy.scan(value, Policy.getInstance("antisamy.xml"), AntiSamy.SAX);
//安全的HTML輸出
value = cr.getCleanHTML();
value = HtmlUtils.htmlEscape(value);
//return cr.getCleanHTML();
} catch (ScanException e) {
e.printStackTrace();
} catch (PolicyException e) {
System.out.println("加載XSS規則文件異常: " + e.getMessage());
}
}
return value;
}
//使用此方法即可
public static String xssClean_New(String value)
{
if (StringUtils.isNotEmpty(value))
{
AntiSamy antiSamy = new AntiSamy();
value = HtmlUtils.htmlEscape(value);
}
return value;
}
public static String xssClean_4(String value) {//界面會報錯
value = StringUtil.UrlDecode(value, "UTF-8");
value = HtmlUtils.htmlEscape(value);
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
return value;
}
/**
* 將容易引起xss漏洞的半角字符直接替換成全角字符
* 清除惡意的XSS腳本
* @param s
* @return
*/
public static String xssClean(String value) {
if (value == null || value.isEmpty()) {
return value;
}
value = StringUtil.UrlDecode(value, "UTF-8");
value = HtmlUtils.htmlEscape(value);
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
StringBuilder sb = new StringBuilder(value.length() + 16);
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
switch (c) {
case '>':
sb.append(">");// 轉義大於號
break;
case '<':
sb.append("<");// 轉義小於號
break;
case '\'':
sb.append("'");// 轉義單引號
break;
case '\"':
sb.append(""");// 轉義雙引號
break;
case '&':
sb.append("&");// 轉義&
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
public static String xssClean_error(String value) {
if (value != null) {
value = value.replaceAll("", "");
// Avoid anything between script tags
Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of expression
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome </script> tag
scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome <script ...> tag
scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid eval(...) expressions
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid expression(...) expressions
scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid javascript:... expressions
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid vbscript:... expressions
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid onload= expressions
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
value = HtmlUtils.htmlEscape(value);
}
return value;
}
}
萬事大吉!他娘的再也不用擔心Appscan掃出來了,我們的道路都是IBM他娘的套路。
