XSS攻擊全稱跨站腳本攻擊,是爲不和層疊樣式表(Cascading Style Sheets, CSS)的縮寫混淆,故將跨站腳本攻擊縮寫爲XSS,XSS是一種在web應用中的計算機安全漏洞,它容許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。前端
簡而言之,就是做惡用戶經過表單提交一些前端代碼,若是不作處理的話,這些前端代碼將會在展現的時候被瀏覽器執行。java
解決XSS攻擊,能夠經過後端對輸入的數據作過濾或者轉義,使XSS攻擊代碼失效。web
對於過濾XSS腳本的代碼,經過搜索引擎能夠搜索到不少,但彷佛都不是那麼全面。基本上都是隻能過濾querystring(表單類型)類型的入參,而不能過濾json類型的入參。其實,在如今的開發中,更多的是使用json類型作數據交互。下面就直接貼代碼了:spring
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* @author Happy
* 防止XSS攻擊
*/
public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
private HttpServletRequest request;
public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = request.getParameter(name);
if (!StringUtils.isEmpty(value)) {
value = StringEscapeUtils.escapeHtml4(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] parameterValues = super.getParameterValues(name);
if (parameterValues == null) {
return null;
}
for (int i = 0; i < parameterValues.length; i++) {
String value = parameterValues[i];
parameterValues[i] = StringEscapeUtils.escapeHtml4(value);
}
return parameterValues;
}
}
複製代碼
這裏重寫了兩個方法:getParameter和getParameterValues,getParameter方法是直接經過request得到querystring類型的入參調用的方法。若是是經過springMVC註解類型來得到參數的話,走的是getParameterValues的方法。你們能夠經過打印一個輸出來驗證一下。apache
StringEscapeUtils.escapeHtml4這個方法來自Apache的工具類,maven座標以下:json
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.4</version>
</dependency>
複製代碼
過濾的代碼寫完了,下面就是在一個filter中應用該代碼。後端
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author Happy
*/
@WebFilter
@Component
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(req);
chain.doFilter(xssRequestWrapper, response);
}
@Override
public void destroy() {
}
/**
* 過濾json類型的
* @param builder
* @return
*/
@Bean
@Primary
public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
//解析器
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
//註冊xss解析器
SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
xssModule.addSerializer(new XssStringJsonSerializer());
objectMapper.registerModule(xssModule);
//返回
return objectMapper;
}
}
複製代碼
過濾表單類型的代碼已經完成(xssObjectMapper這個是後面過濾json類型纔用到的)。下面來實現過濾json類型的代碼:瀏覽器
代碼以下:安全
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.text.StringEscapeUtils;
import java.io.IOException;
public class XssStringJsonSerializer extends JsonSerializer<String> {
@Override
public Class<String> handledType() {
return String.class;
}
@Override
public void serialize(String value, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
if (value != null) {
String encodedValue = StringEscapeUtils.escapeHtml4(value);
jsonGenerator.writeString(encodedValue);
}
}
}
複製代碼
這裏是經過修改SpringMVC的json序列化來達到過濾xss的目的的。其實也能夠經過第一種方法,重寫getInputStream方法來實現,這裏我就不作演示了(經過json類型傳參會走getInputStream方法,經過重寫該方法打印輸出能夠證實)。springboot
TestController.java
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Happy
*/
@RestController
@RequestMapping(value = "/test")
public class TestController {
@PostMapping(value = "/xss")
public Object test(String name) {
System.out.println(name);
return name;
}
@PostMapping(value = "/json")
public Object testJSON(@RequestBody Param param) {
return param;
}
@GetMapping(value = "/query")
public Object testQuery(String q){
return q;
}
@PostMapping(value = "/upload")
public Object upload(MultipartFile file){
System.out.println(file.getOriginalFilename());
return "OK";
}
}
複製代碼
下面經過postman測試下效果:
能夠看到,js代碼已經通過轉義。轉義事後的代碼,即便前端讀取過去了,也不會被瀏覽器執行的。