詳解java解決XSS攻擊常用方法總結
前言
在項目驗收階段,通常會對待驗收項目做一些安全漏洞的測試,比如接口攻擊,並發測試,XSS註入,SQL惡意註入測試,安全越權等操作,這時,就是考驗項目的安全方面是否做的足夠健壯的時候,本篇對XSS腳本攻擊在實際WEB項目中的處理辦法,提供2種可實行的方法
xss攻擊
XSS攻擊通常指的是通過利用網頁開發時留下的漏洞,通過巧妙的方法註入惡意指令代碼到網頁,使用戶加載並執行攻擊者惡意制造的網頁程序。這些惡意網頁程序通常是JavaScript,但實際上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻擊成功後,攻擊者可能得到包括但不限於更高的權限(如執行一些操作)、私密網頁內容、會話和cookie等各種內容
簡單說就是說,通過在輸入框輸入一些js代碼,如在賬號密碼輸入框中輸入
<video src=1 onerror=alert(/xss/)/>
或者
<script>alert("@@") </script>
這樣點擊提交的時候就會觸發alert彈窗,分別彈出 xss 和 @@ 的內容,這裡隻是做個簡單的演示,彈瞭個窗口,還能存儲病毒下載地址到服務端,進入的時候自動下載,或者修改你的cookie啥的,這裡感興趣可以百度查查xss攻擊
解決方式一:強制修改html敏感標簽內容
這是一種相對容易理解的方式,解決思路就是,當惡意註入的字段中,包含瞭類似<script>alert(ooo)</script>
這種html標簽時,後臺程序代碼中強制替換或更改標簽內容,這樣存入數據庫的內容再次返回至頁面時,就不會以html的形式進行執行瞭
下面提供一個工具類
/** * xss特殊字符攔截與過濾 * * @author zhangcy * @date 2021-04016 */ public class XssStrUtils { /** * 濾除content中的危險 HTML 代碼, 主要是腳本代碼, 滾動字幕代碼以及腳本事件處理代碼 * @param content 需要濾除的字符串 * @return 過濾的結果 */ public static String replaceHtmlCode(String content) { if (null == content) return null; if (0 == content.length()) return ""; // 需要濾除的腳本事件關鍵字 String[] eventKeywords = { "onmouseover", "onmouseout", "onmousedown", "onmouseup", "onmousemove", "onclick", "ondblclick", "onkeypress", "onkeydown", "onkeyup", "ondragstart", "onerrorupdate", "onhelp", "onreadystatechange", "onrowenter", "onrowexit", "onselectstart", "onload", "onunload", "onbeforeunload", "onblur", "onerror", "onfocus", "onresize", "onscroll", "oncontextmenu", "alert" }; content = replace(content, "<script", "<script", false); content = replace(content, "</script", "</script", false); content = replace(content, "<marquee", "<marquee", false); content = replace(content, "</marquee", "</marquee", false); content = replace(content, "'", "_", false);// 將單引號替換成下劃線 content = replace(content, "\"", "_", false);// 將雙引號替換成下劃線 // 濾除腳本事件代碼 for (int i = 0; i < eventKeywords.length; i++) { content = replace(content, eventKeywords[i], "_" + eventKeywords[i], false); // 添加一個"_", 使事件代碼無效 } return content; } /** * 將字符串 source 中的 oldStr 替換為 newStr, 並以大小寫敏感方式進行查找 * * @param source 需要替換的源字符串 * @param oldStr 需要被替換的老字符串 * @param newStr 替換為的新字符串 */ private static String replace(String source, String oldStr, String newStr) { return replace(source, oldStr, newStr, true); } /** * 將字符串 source 中的 oldStr 替換為 newStr, matchCase 為是否設置大小寫敏感查找 * * @param source 需要替換的源字符串 * @param oldStr 需要被替換的老字符串 * @param newStr 替換為的新字符串 * @param matchCase 是否需要按照大小寫敏感方式查找 */ private static String replace(String source, String oldStr, String newStr,boolean matchCase) { if (source == null) return null; // 首先檢查舊字符串是否存在, 不存在就不進行替換 if (source.toLowerCase().indexOf(oldStr.toLowerCase()) == -1) return source; int findStartPos = 0; int a = 0; while (a > -1) { int b = 0; String str1, str2, str3, str4, strA, strB; str1 = source; str2 = str1.toLowerCase(); str3 = oldStr; str4 = str3.toLowerCase(); if (matchCase) { strA = str1; strB = str3; } else { strA = str2; strB = str4; } a = strA.indexOf(strB, findStartPos); if (a > -1) { b = oldStr.length(); findStartPos = a + b; StringBuffer bbuf = new StringBuffer(source); source = bbuf.replace(a, a + b, newStr) + ""; // 新的查找開始點位於替換後的字符串的結尾 findStartPos = findStartPos + newStr.length() - b; } } return source; } }
我們用一個實際的接口做一下簡單的測試
@PostMapping("/getAccName") public String getAccName(@RequestBody NameParams params){ String s = XssStrUtils.replaceHtmlCode(params.getName()); return s; }
使用接口工具調一下,
使用這種方式,即使前端惡意註入瞭某些非法的html標簽,經過後端的過濾處理,返回的內容就不會執行html的相關操作事件瞭
解決方式二:利用過濾器過濾非法html標簽
第二種思路,考慮在過濾器中添加對所有請求接口的參數進行參數的攔截過濾,即程序認為的不合法標簽都會自動做過濾,至於過濾的規則,可以借助現有的第三方組件,比如spring框架的htmlUtil類,這裡使用hutool工具集提供的相關API做處理
導入依賴
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.9</version> </dependency>
1、添加自定義過濾器增強包裝類
public class XssHttpRequestWrapper extends HttpServletRequestWrapper { public XssHttpRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); if(!StringUtils.isEmpty(value)){ value = HtmlUtil.filter(value); } return value; } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if(values!=null){ for(int i=0;i<values.length;i++){ String value = values[i]; if(!StringUtils.isEmpty(value)){ value = HtmlUtil.filter(value); } values[i]=value; } } return values; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> parameters = super.getParameterMap(); Map<String, String[]> map = new LinkedHashMap<>(); if(parameters !=null){ for(String key : parameters.keySet()){ String[] values = parameters.get(key); for(int i=0;i<values.length;i++){ String value = values[i]; if(!StringUtils.isEmpty(value)){ value = HtmlUtil.filter(value); } values[i]=value; } map.put(key,values); } } return map; } @Override public String getHeader(String name) { String value = super.getHeader(name); if(!StringUtils.isEmpty(value)){ value = HtmlUtil.filter(value); } return value; } @Override public ServletInputStream getInputStream() throws IOException { InputStream in = super.getInputStream(); InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8")); BufferedReader buffer = new BufferedReader(reader); StringBuffer body = new StringBuffer(); String line = buffer.readLine(); while (line !=null){ body.append(line); line = buffer.readLine(); } buffer.close(); reader.close(); in.close(); Map<String,Object> map = JSONUtil.parseObj(body.toString()); Map<String,Object> result = new LinkedHashMap<>(); for(String key : map.keySet()){ Object val = map.get(key); if(val instanceof String){ if(!StringUtils.isEmpty(val.toString())){ result.put(key,HtmlUtil.filter(val.toString())); } }else { result.put(key,val); } } String json = JSONUtil.toJsonStr(result); ByteArrayInputStream bain = new ByteArrayInputStream(json.getBytes()); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bain.read(); } }; } }
可以理解為,在自定義過濾器中添加上面的增強類,那麼所有請求後端的接口都將先走改類的邏輯,參數進行過濾
2、自定義過濾器並註入全局bean
public class XssFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)servletRequest; XssHttpRequestWrapper requestWrapper = new XssHttpRequestWrapper(request); filterChain.doFilter(requestWrapper,servletResponse); } @Override public void destroy() { } }
@Configuration public class XSSFilterRegister { @Bean public FilterRegistrationBean<XssFilter> RegistTest1(){ //通過FilterRegistrationBean實例設置優先級可以生效 FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<XssFilter>(); bean.setFilter(new XssFilter());//註冊自定義過濾器 bean.setName("flilter");//過濾器名稱 bean.addUrlPatterns("/*");//過濾所有路徑 return bean; } }
仍然使用上面的接口做一下測試
@PostMapping("/getAccName") public String getAccName(@RequestBody NameParams params){ return "name is : "+ params.getName(); }
通過這種方式,直接將註入的敏感標簽符號去掉,這樣確保瞭入庫的數據的安全性,返回給頁面的數據就不存在非法html標簽問題瞭
到此這篇關於詳解java解決XSS攻擊常用方法總結的文章就介紹到這瞭,更多相關java XSS攻擊 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found