前言
在專案驗收階段,通常會對待驗收專案做一些安全漏洞的測驗,比如介面攻擊,并發測驗,XSS注入,SQL惡意注入測驗,安全越權等操作,這時,就是考驗專案的安全方面是否做的足夠健壯的時候,本篇對XSS腳本攻擊在實際WEB專案中的處理辦法,提供2種可實行的方法
xss攻擊
XSS攻擊通常指的是通過利用網頁開發時留下的漏洞,通過巧妙的方法注入惡意指令代碼到網頁,使用戶加載并執行攻擊者惡意制造的網頁程式,這些惡意網頁程式通常是JavaScript,但實際上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML,攻擊成功后,攻擊者可能得到包括但不限于更高的權限(如執行一些操作)、私密網頁內容、會話和cookie等各種內容
簡單說就是說,通過在輸入框輸入一些js代碼,如在賬號密碼輸入框中輸入
<video src=1 one rror=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標簽問題了
本篇到此結束,最后感謝觀看!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277797.html
標籤:其他
