Java過濾XSS腳本攻擊詳細代碼示例
背景
之前公司信息安全部門對公司項目進行網(wǎng)絡安全升級時,發(fā)現(xiàn)項目里可能會出現(xiàn)XSS腳本攻擊漏洞,所以就需要對其參數(shù)進行過濾攔截。
XSS
百度百科:XSS攻擊全稱:cross site scripting(這里是為了和CSS區(qū)分,所以叫XSS),跨站腳本攻擊(XSS),是最普遍的Web應用安全漏洞。這類漏洞能夠使得攻擊者嵌入惡意腳本代碼到正常用戶會訪問到的頁面中,當正常用戶訪問該頁面時,則可導致嵌入的惡意腳本代碼的執(zhí)行,從而達到惡意攻擊用戶的目的。攻擊者可以使用戶在瀏覽器中執(zhí)行其預定義的惡意腳本,其導致的危害可想而知,如劫持用戶會話,插入惡意內容、重定向用戶、使用惡意軟件劫持用戶瀏覽器、繁殖XSS蠕蟲,甚至破壞網(wǎng)站、修改路由器配置信息等。
- xss漏洞攻擊分為三種:
- 反射性XSS攻擊:前端在發(fā)送請求時,在url參數(shù)里攜帶一些腳本命令,然后等服務端把腳本在反射給瀏覽器執(zhí)行腳本代碼,進行XSS漏洞攻擊
- 存儲性XSS攻擊:和反射性XSS漏洞攻擊相似,但是服務器會進行持久化保存腳本命令,后續(xù)用戶訪問該數(shù)據(jù)時持久性進行XSS漏洞攻擊
- DOS性XSS攻擊:和服務端沒有交互,靠瀏覽器的DOM解析進行XSS攻擊
Java過濾
- 預防XSS漏洞攻擊除了web端進行解析過濾外,也還需要服務端進行校驗過濾
- 本次使用springboot項目中過濾器進行對前端傳來的參數(shù)進行過濾攔截
/**
* springboot注冊過濾器
*
* @author: zrh
* @date: 2021-11-17
*/
@Configuration
public class XssFilterConfig {
@Bean
public FilterRegistrationBean xssFilterRegistrationBean () {
FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean();
// 設置自定義過濾器
initXssFilterBean.setFilter(new XssFilter());
// 設置優(yōu)先級(值越低,優(yōu)先級越高)
initXssFilterBean.setOrder(1);
// 設置過濾路徑
initXssFilterBean.addUrlPatterns("/*");
// 設置過濾器名稱
initXssFilterBean.setName("XSS_filter");
// 設置過濾器作用范圍(可以配置多種,這里指定過濾請求資源)
initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST);
return initXssFilterBean;
}
}
- spring項目中可以使用web.xml定義過濾器,springboot中沒有web.xml配置文件,那么就可以使用FilterRegistrationBean類把過濾器實例注冊到容器
/**
* 自定義過濾器
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public class XssFilter implements Filter {
@Override
public void init (FilterConfig filterConfig) {
// 初始化調用
}
@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 () {
// 銷毀調用
}
}
/**
* 重寫請求參數(shù)過濾
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper (HttpServletRequest request) {
super(request);
}
/**
* 對GET請求中參數(shù)進行過濾校驗
*
* @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;
}
/**
* 對POST請求頭進行參數(shù)過濾校驗
*
* @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);
}
/**
* 對POST請求中body參數(shù)進行校驗
*
* @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) {
}
};
}
/**
* 解析請求流參數(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 關閉異常 e:", e);
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
log.error("reader 關閉異常 e:", e);
}
}
}
final String param = XssUtil.filterBody(sb.toString());
log.info("getInputStream -> 過濾前參數(shù):{},過濾后參數(shù):{}", sb, param);
return param;
}
}
- 重寫HttpServletRequestWrapper類用于過濾改變請求參數(shù)值
/**
* 參數(shù)校驗工具類
*
* @author: zrh
* @date: 2021-11-17
*/
@Slf4j
public final class XssUtil {
private XssUtil () {
}
/**
* 網(wǎng)上找的XSS匹配正則表達式
*/
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),
// 空格英文單雙引號
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))")
};
/**
* 對請求對象參數(shù)進行過濾校驗
*
* @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;
}
/**
* 對請求字符串參數(shù)進行過濾校驗
*
* @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";
}
}
模擬請求響應結果:
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":""}
最后
- 除了可以使用過濾器以外,還可以使用攔截器進行攔截校驗。大致流程也差不多,先獲取參數(shù),然后進行校驗,最后重新賦值。
- 上述代碼例子只是簡單粗化版,在實際項目中要根據(jù)需求進行代碼調整和性能優(yōu)化后才可在線上使用。
到此這篇關于Java過濾XSS腳本攻擊的文章就介紹到這了,更多相關Java過濾XSS腳本攻擊內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何實現(xiàn)Java中一個簡單的LinkedList
LinkedList與ArrayList都是List接口的具體實現(xiàn)類。下面將介紹如何實現(xiàn)一個簡單的LinkedList,具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02

