新聞中心
?SQL注入是常見的系統(tǒng)安全問(wèn)題之一,用戶通過(guò)特定方式向系統(tǒng)發(fā)送SQL腳本,可直接自定義操作系統(tǒng)數(shù)據(jù)庫(kù),如果系統(tǒng)沒(méi)有對(duì)SQL注入進(jìn)行攔截,那么用戶甚至可以直接對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪改查等操作。

??XSS全稱為Cross Site Script跨站點(diǎn)腳本攻擊,和SQL注入類似,都是通過(guò)特定方式向系統(tǒng)發(fā)送攻擊腳本,對(duì)系統(tǒng)進(jìn)行控制和侵害。SQL注入主要以攻擊數(shù)據(jù)庫(kù)來(lái)達(dá)到攻擊系統(tǒng)的目的,而XSS則是以惡意執(zhí)行前端腳本來(lái)攻擊系統(tǒng)。
??項(xiàng)目框架中使用mybatis/mybatis-plus數(shù)據(jù)持久層框架,在使用過(guò)程中,已有規(guī)避SQL注入的規(guī)則和使用方法。但是在實(shí)際開發(fā)過(guò)程中,由于各種原因,開發(fā)人員對(duì)持久層框架的掌握水平不同,有些特殊業(yè)務(wù)情況必須從前臺(tái)傳入SQL腳本。這時(shí)就需要對(duì)系統(tǒng)進(jìn)行加固,防止特殊情況下引起的系統(tǒng)風(fēng)險(xiǎn)。
??在微服務(wù)架構(gòu)下,我們考慮如何實(shí)現(xiàn)SQL注入/XSS攻擊攔截時(shí),肯定不會(huì)在每個(gè)微服務(wù)都實(shí)現(xiàn)一遍SQL注入/XSS攻擊攔截。根據(jù)我們微服務(wù)系統(tǒng)的設(shè)計(jì),所有的請(qǐng)求都會(huì)經(jīng)過(guò)Gateway網(wǎng)關(guān),所以在實(shí)現(xiàn)時(shí)就可以參照前面的日志攔截器來(lái)實(shí)現(xiàn)。在接收到一個(gè)請(qǐng)求時(shí),通過(guò)攔截器解析請(qǐng)求參數(shù),判斷是否有SQL注入/XSS攻擊參數(shù),如果有,那么返回異常即可。
??我們前面在對(duì)微服務(wù)Gateway進(jìn)行自定義擴(kuò)展時(shí),增加了Gateway插件功能。我們會(huì)根據(jù)系統(tǒng)需求開發(fā)各種Gateway功能擴(kuò)展插件,并且可以根據(jù)系統(tǒng)配置文件來(lái)啟用/禁用這些插件。下面我們就將防止SQL注入/XSS攻擊攔截器作為一個(gè)Gateway插件來(lái)開發(fā)和配置。
1、新增SqlInjectionFilter 過(guò)濾器和XssInjectionFilter過(guò)濾器,分別用于解析請(qǐng)求參數(shù)并對(duì)參數(shù)進(jìn)行判斷是否存在SQL注入/XSS攻腳本。此處有公共判斷方法,通過(guò)配置文件來(lái)讀取請(qǐng)求的過(guò)濾配置,因?yàn)椴皇嵌嘤械恼?qǐng)求都會(huì)引發(fā)SQL注入和XSS攻擊,如果無(wú)差別的全部攔截和請(qǐng)求,那么勢(shì)必影響到系統(tǒng)的性能。
判斷SQL注入的攔截器
/**
* 防sql注入
* @author GitEgg
*/
@Log4j2
@AllArgsConstructor
public class SqlInjectionFilter implements GlobalFilter, Ordered {
......
// 當(dāng)返回參數(shù)為true時(shí),解析請(qǐng)求參數(shù)和返回參數(shù)
if (shouldSqlInjection(exchange))
{
MultiValueMapqueryParams = request.getQueryParams();
boolean chkRetGetParams = SqlInjectionRuleUtils.mapRequestSqlKeyWordsCheck(queryParams);
boolean chkRetJson = false;
boolean chkRetFormData = false;
HttpHeaders headers = request.getHeaders();
MediaType contentType = headers.getContentType();
long length = headers.getContentLength();
if(length > 0 && null != contentType && (contentType.includes(MediaType.APPLICATION_JSON)
||contentType.includes(MediaType.APPLICATION_JSON_UTF8))){
chkRetJson = SqlInjectionRuleUtils.jsonRequestSqlKeyWordsCheck(gatewayContext.getRequestBody());
}
if(length > 0 && null != contentType && contentType.includes(MediaType.APPLICATION_FORM_URLENCODED)){
log.debug("[RequestLogFilter](Request)FormData:{}",gatewayContext.getFormData());
chkRetFormData = SqlInjectionRuleUtils.mapRequestSqlKeyWordsCheck(gatewayContext.getFormData());
}
if (chkRetGetParams || chkRetJson || chkRetFormData)
{
return WebfluxResponseUtils.responseWrite(exchange, "參數(shù)中不允許存在sql關(guān)鍵字");
}
return chain.filter(exchange);
}
else {
return chain.filter(exchange);
}
}
......
}
- 判斷XSS攻擊的攔截器
/**
* 防xss注入
* @author GitEgg
*/
@Log4j2
@AllArgsConstructor
public class XssInjectionFilter implements GlobalFilter, Ordered {
......
// 當(dāng)返回參數(shù)為true時(shí),記錄請(qǐng)求參數(shù)和返回參數(shù)
if (shouldXssInjection(exchange))
{
MultiValueMapqueryParams = request.getQueryParams();
boolean chkRetGetParams = XssInjectionRuleUtils.mapRequestSqlKeyWordsCheck(queryParams);
boolean chkRetJson = false;
boolean chkRetFormData = false;
HttpHeaders headers = request.getHeaders();
MediaType contentType = headers.getContentType();
long length = headers.getContentLength();
if(length > 0 && null != contentType && (contentType.includes(MediaType.APPLICATION_JSON)
||contentType.includes(MediaType.APPLICATION_JSON_UTF8))){
chkRetJson = XssInjectionRuleUtils.jsonRequestSqlKeyWordsCheck(gatewayContext.getRequestBody());
}
if(length > 0 && null != contentType && contentType.includes(MediaType.APPLICATION_FORM_URLENCODED)){
log.debug("[RequestLogFilter](Request)FormData:{}",gatewayContext.getFormData());
chkRetFormData = XssInjectionRuleUtils.mapRequestSqlKeyWordsCheck(gatewayContext.getFormData());
}
if (chkRetGetParams || chkRetJson || chkRetFormData)
{
return WebfluxResponseUtils.responseWrite(exchange, "參數(shù)中不允許存在XSS注入關(guān)鍵字");
}
return chain.filter(exchange);
}
else {
return chain.filter(exchange);
}
}
......
}
2、新增SqlInjectionRuleUtils工具類和XssInjectionRuleUtils工具類,通過(guò)正則表達(dá)式,用于判斷參數(shù)是否屬于SQL注入/XSS攻擊腳本。
- 通過(guò)正則表達(dá)式對(duì)參數(shù)進(jìn)行是否有SQL注入風(fēng)險(xiǎn)的判斷
/**
* 防sql注入工具類
* @author GitEgg
*/
@Slf4j
public class SqlInjectionRuleUtils {
/**
* SQL的正則表達(dá)式
*/
private static String badStrReg = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
/**
* SQL的正則表達(dá)式
*/
private static Pattern sqlPattern = Pattern.compile(badStrReg, Pattern.CASE_INSENSITIVE);
/**
* sql注入校驗(yàn) map
*
* @param map
* @return
*/
public static boolean mapRequestSqlKeyWordsCheck(MultiValueMapmap) {
//對(duì)post請(qǐng)求參數(shù)值進(jìn)行sql注入檢驗(yàn)
return map.entrySet().stream().parallel().anyMatch(entry -> {
//這里需要將參數(shù)轉(zhuǎn)換為小寫來(lái)處理
String lowerValue = Optional.ofNullable(entry.getValue())
.map(Object::toString)
.map(String::toLowerCase)
.orElse("");
if (sqlPattern.matcher(lowerValue).find()) {
log.error("參數(shù)[{}]中包含不允許sql的關(guān)鍵詞", lowerValue);
return true;
}
return false;
});
}
/**
* sql注入校驗(yàn) json
*
* @param value
* @return
*/
public static boolean jsonRequestSqlKeyWordsCheck(String value) {
if (JSONUtil.isJsonObj(value)) {
JSONObject json = JSONUtil.parseObj(value);
Mapmap = json;
//對(duì)post請(qǐng)求參數(shù)值進(jìn)行sql注入檢驗(yàn)
return map.entrySet().stream().parallel().anyMatch(entry -> {
//這里需要將參數(shù)轉(zhuǎn)換為小寫來(lái)處理
String lowerValue = Optional.ofNullable(entry.getValue())
.map(Object::toString)
.map(String::toLowerCase)
.orElse("");
if (sqlPattern.matcher(lowerValue).find()) {
log.error("參數(shù)[{}]中包含不允許sql的關(guān)鍵詞", lowerValue);
return true;
}
return false;
});
} else {
JSONArray json = JSONUtil.parseArray(value);
List
- 通過(guò)正則表達(dá)式對(duì)參數(shù)進(jìn)行是否有XSS攻擊風(fēng)險(xiǎn)的判斷
/**
* XSS注入過(guò)濾工具類
* @author GitEgg
*/
public class XssInjectionRuleUtils {
private static final Pattern[] PATTERNS = {
// Avoid anything in a ", Pattern.CASE_INSENSITIVE),
// Avoid anything in a src='...' type of expression
Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// Remove any lonesome tag
Pattern.compile("", Pattern.CASE_INSENSITIVE),
// Avoid anything in a


咨詢
建站咨詢