Java過濾XSS腳本攻擊詳細(xì)代碼示例
背景
之前公司信息安全部門對(duì)公司項(xiàng)目進(jìn)行網(wǎng)絡(luò)安全升級(jí)時(shí),發(fā)現(xiàn)項(xiàng)目里可能會(huì)出現(xiàn)XSS腳本攻擊漏洞,所以就需要對(duì)其參數(shù)進(jìn)行過濾攔截。
XSS
百度百科:XSS攻擊全稱:cross site scripting(這里是為了和CSS區(qū)分,所以叫XSS),跨站腳本攻擊(XSS),是最普遍的Web應(yīng)用安全漏洞。這類漏洞能夠使得攻擊者嵌入惡意腳本代碼到正常用戶會(huì)訪問到的頁面中,當(dāng)正常用戶訪問該頁面時(shí),則可導(dǎo)致嵌入的惡意腳本代碼的執(zhí)行,從而達(dá)到惡意攻擊用戶的目的。攻擊者可以使用戶在瀏覽器中執(zhí)行其預(yù)定義的惡意腳本,其導(dǎo)致的危害可想而知,如劫持用戶會(huì)話,插入惡意內(nèi)容、重定向用戶、使用惡意軟件劫持用戶瀏覽器、繁殖XSS蠕蟲,甚至破壞網(wǎng)站、修改路由器配置信息等。
- xss漏洞攻擊分為三種:
- 反射性XSS攻擊:前端在發(fā)送請(qǐng)求時(shí),在url參數(shù)里攜帶一些腳本命令,然后等服務(wù)端把腳本在反射給瀏覽器執(zhí)行腳本代碼,進(jìn)行XSS漏洞攻擊
- 存儲(chǔ)性XSS攻擊:和反射性XSS漏洞攻擊相似,但是服務(wù)器會(huì)進(jìn)行持久化保存腳本命令,后續(xù)用戶訪問該數(shù)據(jù)時(shí)持久性進(jìn)行XSS漏洞攻擊
- DOS性XSS攻擊:和服務(wù)端沒有交互,靠瀏覽器的DOM解析進(jìn)行XSS攻擊
Java過濾
- 預(yù)防XSS漏洞攻擊除了web端進(jìn)行解析過濾外,也還需要服務(wù)端進(jìn)行校驗(yàn)過濾
- 本次使用springboot項(xiàng)目中過濾器進(jìn)行對(duì)前端傳來的參數(shù)進(jìn)行過濾攔截
/**
* springboot注冊(cè)過濾器
*
* @author: zrh
* @date: 2021-11-17
*/
@Configuration
public class XssFilterConfig {
@Bean
public FilterRegistrationBean xssFilterRegistrationBean () {
FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean();
// 設(shè)置自定義過濾器
initXssFilterBean.setFilter(new XssFilter());
// 設(shè)置優(yōu)先級(jí)(值越低,優(yōu)先級(jí)越高)
initXssFilterBean.setOrder(1);
// 設(shè)置過濾路徑
initXssFilterBean.addUrlPatterns("/*");
// 設(shè)置過濾器名稱
initXssFilterBean.setName("XSS_filter");
// 設(shè)置過濾器作用范圍(可以配置多種,這里指定過濾請(qǐng)求資源)
initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST);
return initXssFilterBean;
}
}
- spring項(xiàng)目中可以使用web.xml定義過濾器,springboot中沒有web.xml配置文件,那么就可以使用FilterRegistrationBean類把過濾器實(shí)例注冊(cè)到容器
/**
* 自定義過濾器
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public class XssFilter implements Filter {
@Override
public void init (FilterConfig filterConfig) {
// 初始化調(diào)用
}
@Override
public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final XssHttpServletRequestWrapper requestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void destroy () {
// 銷毀調(diào)用
}
}
/**
* 重寫請(qǐng)求參數(shù)過濾
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper (HttpServletRequest request) {
super(request);
}
/**
* 對(duì)GET請(qǐng)求中參數(shù)進(jìn)行過濾校驗(yàn)
*
* @param name
* @return
*/
@Override
public String[] getParameterValues (String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return null;
}
int count = values.length;
String[] cleanParams = new String[count];
for (int i = 0; i < count; i++) {
cleanParams[i] = String.valueOf(XssUtil.filterParam(values[i]));
log.info("getParameterValues -> name:{},過濾前參數(shù):{},過濾后參數(shù):{}", name, values[i], cleanParams[i]);
}
return cleanParams;
}
/**
* 對(duì)POST請(qǐng)求頭進(jìn)行參數(shù)過濾校驗(yàn)
*
* @param header
* @return
*/
@Override
public Enumeration getHeaders (String header) {
final String value = super.getHeader(header);
final LinkedList list = new LinkedList();
if (value != null) {
final Object param = XssUtil.filterParam(value);
list.addFirst(param);
log.info("getHeaders -> header:{},過濾前參數(shù):{},過濾后參數(shù):{}", header, value, param);
}
return Collections.enumeration(list);
}
/**
* 對(duì)POST請(qǐng)求中body參數(shù)進(jìn)行校驗(yàn)
*
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream () throws IOException {
final ByteArrayInputStream stream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes());
return new ServletInputStream() {
@Override
public int read () {
return stream.read();
}
@Override
public boolean isFinished () {
return false;
}
@Override
public boolean isReady () {
return false;
}
@Override
public void setReadListener (ReadListener readListener) {
}
};
}
/**
* 解析請(qǐng)求流參數(shù)
*
* @param servletInputStream
* @return
*/
public String inputHandlers (ServletInputStream servletInputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("異常 e:", e);
} finally {
if (servletInputStream != null) {
try {
servletInputStream.close();
} catch (IOException e) {
log.error("servletInputStream 關(guān)閉異常 e:", e);
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.error("reader 關(guān)閉異常 e:", e);
}
}
}
final String param = XssUtil.filterBody(sb.toString());
log.info("getInputStream -> 過濾前參數(shù):{},過濾后參數(shù):{}", sb, param);
return param;
}
}
- 重寫HttpServletRequestWrapper類用于過濾改變請(qǐng)求參數(shù)值
/**
* 參數(shù)校驗(yàn)工具類
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public final class XssUtil {
private XssUtil () {
}
/**
* 網(wǎng)上找的XSS匹配正則表達(dá)式
*/
private final static Pattern[] PATTERNS = new Pattern[]{
// Script fragments
Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
// src='...'
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),
// lonely script tags
Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// eval(...)
Pattern.compile("eval\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// expression(...)
Pattern.compile("expression\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// javascript:...
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
// vbscript:...
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
// 空格英文單雙引號(hào)
Pattern.compile("[\s'"]+", Pattern.CASE_INSENSITIVE),
// onload(...)=...
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// alert
Pattern.compile("alert(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL),
//Checks any html tags i.e. <script, <embed, <object etc.
Pattern.compile("(<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))")
};
/**
* 對(duì)請(qǐng)求對(duì)象參數(shù)進(jìn)行過濾校驗(yàn)
*
* @param params
* @return
*/
public static String filterBody (String params) {
try {
if (StringUtils.isBlank(params)) {
return params;
}
final Map<String, Object> map = JSONObject.parseObject(params, Map.class);
if (map.isEmpty()) {
return params;
}
// 參數(shù)過濾
final Iterator<Map.Entry<String, Object>> iterator = map.entrySet().stream().iterator();
while (iterator.hasNext()) {
final Map.Entry<String, Object> next = iterator.next();
next.setValue(filterParam(next.getValue()));
}
return JSON.toJSONString(map);
} catch (Exception e) {
log.error("XSS過濾異常:", e);
}
return params;
}
/**
* 對(duì)請(qǐng)求字符串參數(shù)進(jìn)行過濾校驗(yàn)
*
* @param param
* @param <T>
* @return
*/
public static <T> Object filterParam (T param) {
if (param instanceof String) {
try {
String value = String.valueOf(param);
for (Pattern pattern : PATTERNS) {
value = pattern.matcher(value).replaceAll("");
}
return value;
} catch (Exception e) {
log.error("XSS參數(shù)過濾異常:", e);
}
}
return param;
}
}
- XSS過濾參數(shù)的正則工具類
/**
*
* @Author: ZRH
* @Date: 2021/11/17
*/
@RestController
public class XssTest {
@PostMapping("/xss/test")
public String test (@RequestBody JSONObject jsonObject) {
System.out.println(jsonObject.toJSONString());
return "OK";
}
@GetMapping("/xss/test")
public String test (@RequestParam Integer data, @RequestParam String result) {
System.out.println(data);
return "OK";
}
}
模擬請(qǐng)求響應(yīng)結(jié)果:
17:07:06.597 - [http-nio-8888-exec-1] - getHeaders -> header:content-type,過濾前參數(shù):application/json,過濾后參數(shù):application/json
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:token,過濾前參數(shù):123,過濾后參數(shù):123
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:a,過濾前參數(shù):123,過濾后參數(shù):123
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:b,過濾前參數(shù):<script>alert("XSS");</script>,過濾后參數(shù):
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:content-length,過濾前參數(shù):67,過濾后參數(shù):67
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:host,過濾前參數(shù):localhost:8888,過濾后參數(shù):localhost:8888
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:connection,過濾前參數(shù):Keep-Alive,過濾后參數(shù):Keep-Alive
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:user-agent,過濾前參數(shù):Apache-HttpClient/4.5.12 (Java/11.0.8),過濾后參數(shù):Apache-HttpClient/4.5.12(Java/11.0.8)
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:accept-encoding,過濾前參數(shù):gzip,deflate,過濾后參數(shù):gzip,deflate
17:07:06.648 - [http-nio-8888-exec-1] - getInputStream -> 過濾前參數(shù):{ "a": "1", "b": 2, "c": "<script>alert(\"XSS\");</script>"},過濾后參數(shù):{"a":"1","b":2,"c":""}
{"a":"1","b":2,"c":""}
最后
- 除了可以使用過濾器以外,還可以使用攔截器進(jìn)行攔截校驗(yàn)。大致流程也差不多,先獲取參數(shù),然后進(jìn)行校驗(yàn),最后重新賦值。
- 上述代碼例子只是簡單粗化版,在實(shí)際項(xiàng)目中要根據(jù)需求進(jìn)行代碼調(diào)整和性能優(yōu)化后才可在線上使用。
到此這篇關(guān)于Java過濾XSS腳本攻擊的文章就介紹到這了,更多相關(guān)Java過濾XSS腳本攻擊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀圖文分析
這篇文章主要介紹了Java遞歸運(yùn)行的機(jī)制:遞歸的微觀解讀,結(jié)合圖文形式詳細(xì)分析了java遞歸運(yùn)行的原理、機(jī)制與相關(guān)注意事項(xiàng),需要的朋友可以參考下2020-03-03
如何實(shí)現(xiàn)Java中一個(gè)簡單的LinkedList
LinkedList與ArrayList都是List接口的具體實(shí)現(xiàn)類。下面將介紹如何實(shí)現(xiàn)一個(gè)簡單的LinkedList,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-02-02
基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程
這篇文章主要介紹了基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09
java去除中文括號(hào)小括號(hào),或者英文括號(hào)的實(shí)例代碼
這篇文章主要介紹了java去除中文括號(hào)小括號(hào),或者英文括號(hào)的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09

