SpringBoot如何使用過濾器進行XSS防御
在Spring Boot中,我們可以使用注解的方式來進行XSS防御。注解是一種輕量級的防御手段,它可以在方法或字段級別對輸入進行校驗,從而防止XSS攻擊。
而想對全局的請求都進行XSS防御可以使用servlet中的過濾器或者spring mvc中的攔截器,這里使用servlet中的過濾器進行演示。
引入相關(guān)依賴
maven依賴:
<!--JSR-303/JSR-380用于驗證的注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.6.7</version> </dependency>
如果是使用grade,引入依賴:
implementation 'org.springframework.boot:spring-boot-starter-validation:2.6.7'
修改配置文件
xss: enabled: true excludeUrlList: - /xss/local/test
定義配置文件對應(yīng)的屬性類
package com.morris.spring.boot.module.xss; import lombok.Data; import java.util.List; @Data public class XssFilterProperties { /** * 是否啟用XSS過濾。 */ private boolean enabled = true; /** * 需要排除的URL模式,這些URL不會進行XSS過濾。 */ private List<String> excludeUrlList; }
注入XSS配置類
package com.morris.spring.boot.module.xss; import lombok.Data; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import javax.servlet.DispatcherType; @Data @Configuration public class XssFilterConfig { @ConfigurationProperties(prefix = "xss") @Bean public XssFilterProperties xssFilterProperties() { return new XssFilterProperties(); } /** * 注冊XSS過濾器。 * * @return FilterRegistrationBean 用于注冊過濾器的bean。 */ @Bean public FilterRegistrationBean<XssFilter> xssFilterRegistration(XssFilterProperties xssFilterProperties) { FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>(); // 設(shè)置過濾器的分發(fā)類型為請求類型 registrationBean.setDispatcherTypes(DispatcherType.REQUEST); // 創(chuàng)建XssFilter的實例 registrationBean.setFilter(new XssFilter(xssFilterProperties)); // 添加過濾器需要攔截的URL模式,這里攔截所有請求 registrationBean.addUrlPatterns("/*"); // 設(shè)置過濾器的名稱 registrationBean.setName("XssFilter"); // 設(shè)置過濾器的執(zhí)行順序,數(shù)值越小,優(yōu)先級越高 registrationBean.setOrder(9999); return registrationBean; } @Bean public HttpMessageConverters xssHttpMessageConverters() { XSSMappingJackson2HttpMessageConverter xssMappingJackson2HttpMessageConverter = new XSSMappingJackson2HttpMessageConverter(); HttpMessageConverter converter = xssMappingJackson2HttpMessageConverter; return new HttpMessageConverters(converter); } }
XssFilter過濾器
XssFilter過濾器會將所有需要進行防御的請求包裝為XssWrapper。
package com.morris.spring.boot.module.xss; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; @Slf4j public class XssFilter implements Filter { private final XssFilterProperties xssFilterProperties; public XssFilter(XssFilterProperties xssFilterProperties) { this.xssFilterProperties = xssFilterProperties; } /** * 執(zhí)行過濾邏輯,如果當(dāng)前請求不在排除列表中,則通過XSS過濾器包裝請求。 * * @param request HTTP請求對象。 * @param response HTTP響應(yīng)對象。 * @param chain 過濾器鏈對象,用于繼續(xù)或中斷請求處理。 * @throws IOException 如果處理過程中出現(xiàn)I/O錯誤。 * @throws ServletException 如果處理過程中出現(xiàn)Servlet相關(guān)錯誤。 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; //如果該訪問接口在排除列表里面則不攔截 if (isExcludeUrl(req.getServletPath())) { chain.doFilter(request, response); return; } log.info("uri:{}", req.getRequestURI()); // xss 過濾 chain.doFilter(new XssWrapper(req), resp); } /** * 判斷當(dāng)前請求的URL是否應(yīng)該被排除在XSS過濾之外。 * * @param urlPath 請求的URL路徑。 * @return 如果請求應(yīng)該被排除,則返回true;否則返回false。 */ private boolean isExcludeUrl(String urlPath) { if (!xssFilterProperties.isEnabled()) { //如果xss開關(guān)關(guān)閉了,則所有url都不攔截 return true; } if(CollectionUtils.isEmpty(xssFilterProperties.getExcludeUrlList())) { return false; } for (String pattern : xssFilterProperties.getExcludeUrlList()) { Pattern p = Pattern.compile("^" + pattern); Matcher m = p.matcher(urlPath); if (m.find()) { return true; } } return false; } }
XssWrapper過濾get請求和請求頭
XssWrapper會過濾get請求和請求頭中的非法字符。
package com.morris.spring.boot.module.xss; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; @Slf4j public class XssWrapper extends HttpServletRequestWrapper { public XssWrapper(HttpServletRequest request) { super(request); } /** * 對數(shù)組參數(shù)進行特殊字符過濾 */ @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = XssUtil.clean(values[i]); } return encodedValues; } /** * 對參數(shù)中特殊字符進行過濾 */ @Override public String getParameter(String name) { String value = super.getParameter(name); if (StringUtils.isBlank(value)) { return value; } return XssUtil.clean(value); } /** * 獲取attribute,特殊字符過濾 */ @Override public Object getAttribute(String name) { Object value = super.getAttribute(name); if (value instanceof String && StringUtils.isNotBlank((String) value)) { return XssUtil.clean((String) value); } return value; } /** * 對請求頭部進行特殊字符過濾 */ @Override public String getHeader(String name) { String value = super.getHeader(name); if (StringUtils.isBlank(value)) { return value; } return XssUtil.clean(value); } }
MessageConverter過濾post請求
package com.morris.spring.boot.module.xss; import com.fasterxml.jackson.databind.JavaType; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import java.io.IOException; import java.lang.reflect.Type; /** * 在讀取和寫入JSON數(shù)據(jù)時特殊字符避免xss攻擊的消息解析器 * */ public class XSSMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { /** * 從HTTP輸入消息中讀取對象,同時應(yīng)用XSS防護。 * * @param type 類型令牌,表示要讀取的對象類型。 * @param contextClass 上下文類,提供類型解析的上下文信息。 * @param inputMessage HTTP輸入消息,包含要讀取的JSON數(shù)據(jù)。 * @return 從輸入消息中解析出的對象,經(jīng)過XSS防護處理。 * @throws IOException 如果發(fā)生I/O錯誤。 * @throws HttpMessageNotReadableException 如果消息無法讀取。 */ @Override public Object read(Type type, Class contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { JavaType javaType = getJavaType(type, contextClass); Object obj = readJavaType(javaType, inputMessage); //得到請求json String json = super.getObjectMapper().writeValueAsString(obj); //過濾特殊字符 String result = XssUtil.clean(json); Object resultObj = super.getObjectMapper().readValue(result, javaType); return resultObj; } /** * 從HTTP輸入消息中讀取指定Java類型的對象,內(nèi)部使用。 * * @param javaType 要讀取的對象的Java類型。 * @param inputMessage HTTP輸入消息,包含要讀取的JSON數(shù)據(jù)。 * @return 從輸入消息中解析出的對象。 * @throws IOException 如果發(fā)生I/O錯誤。 * @throws HttpMessageNotReadableException 如果消息無法讀取。 */ private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { try { return super.getObjectMapper().readValue(inputMessage.getBody(), javaType); } catch (IOException ex) { throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); } } /** * 將對象寫入HTTP輸出消息,同時應(yīng)用XSS防護。 * * @param object 要寫入的對象。 * @param outputMessage HTTP輸出消息,對象將被序列化為JSON并寫入此消息。 * @throws IOException 如果發(fā)生I/O錯誤。 * @throws HttpMessageNotWritableException 如果消息無法寫入。 */ @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //得到要輸出的json String json = super.getObjectMapper().writeValueAsString(object); //過濾特殊字符 String result = XssUtil.clean(json); // 輸出 outputMessage.getBody().write(result.getBytes()); } }
Xss過濾工具類
package com.morris.spring.boot.module.xss; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.safety.Whitelist; /** * XSS過濾工具類,使用Jsoup庫對輸入的字符串進行XSS攻擊防護 */ public class XssUtil { /** * 使用jsoup自帶的relaxed白名單 */ private static final Whitelist WHITE_LIST = Whitelist.relaxed(); /** * 定義輸出設(shè)置,關(guān)閉prettyPrint(prettyPrint=false),目的是避免在清理過程中對代碼進行格式化 * 從而保持輸入和輸出內(nèi)容的一致性。 */ private static final Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false); /* 初始化白名單策略,允許所有標(biāo)簽擁有style屬性。 這是因為在富文本編輯中,樣式通常通過style屬性來定義,需要確保這些樣式能夠被保留。 */ static { // 富文本編輯時一些樣式是使用 style 來進行實現(xiàn)的 // 比如紅色字體 style="color:red;" // 所以需要給所有標(biāo)簽添加 style 屬性 WHITE_LIST.addAttributes(":all", "style"); } /** * 清理輸入的字符串,移除潛在的XSS攻擊代碼。 * * @param content 待清理的字符串,通常是用戶輸入的HTML內(nèi)容。 * @return 清理后的字符串,保證不包含XSS攻擊代碼。 */ public static String clean(String content) { // 使用定義好的白名單策略和輸出設(shè)置清理輸入的字符串 return Jsoup.clean(content, "", WHITE_LIST, OUTPUT_SETTINGS); } }
get請求測試
package com.morris.spring.boot.module.xss; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Xss2防御get請求 */ @RestController @RequestMapping("/xss/global") @Validated public class XssGlobalGetController { /** * 使用注解攔截get請求中的xss,在方法參數(shù)前面加上@Xss,注意類上面要加上@Validated注解 * * @param userAccount 請求參數(shù) * @return 請求參數(shù) */ @GetMapping("/test") public String test(String userAccount) { return userAccount; } }
發(fā)送get請求:http://localhost:8888/xss/global/test?userAccount=demoData
返回結(jié)果:demoData
post請求測試
package com.morris.spring.boot.module.xss; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Xss全局防御post請求 */ @RestController @RequestMapping("/xss/global") public class XssGlobalPostController { /** * 使用注解攔截POST請求中的xss,在實體類需要攔截xss的屬性上面加上@Xss或者@Validated注解 * * @param userGlobalLoginPojo 實體類 * @return 實體類 */ @PostMapping("/test") public UserGlobalLoginPojo test(@RequestBody UserGlobalLoginPojo userGlobalLoginPojo) { return userGlobalLoginPojo; } }
發(fā)送post請求:http://localhost:8888/xss/global/test
請求體:
{
"userAccount": "<iframe οnlοad='alert(0)'>demoData</iframe>"
}
返回結(jié)果:
{
"userAccount": "demoData"
}
到此這篇關(guān)于SpringBoot如何使用過濾器進行XSS防御的文章就介紹到這了,更多相關(guān)SpringBoot XSS防御內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?深入理解創(chuàng)建型設(shè)計模式之原型模式
原型(Prototype)模式的定義如下:用一個已經(jīng)創(chuàng)建的實例作為原型,通過復(fù)制該原型對象來創(chuàng)建一個和原型相同或相似的新對象。在這里,原型實例指定了要創(chuàng)建的對象的種類。用這種方式創(chuàng)建對象非常高效,根本無須知道對象創(chuàng)建的細節(jié)2022-02-02Java并發(fā)編程之Semaphore(信號量)詳解及實例
這篇文章主要介紹了Java并發(fā)編程之Semaphore(信號量)詳解及實例的相關(guān)資料,需要的朋友可以參考下2017-06-06Java程序連接數(shù)據(jù)庫的常用的類和接口介紹
這篇文章主要介紹了Java程序連接數(shù)據(jù)庫的常用的類和接口,包括Connection類和Statement類等,需要的朋友可以參考下2015-10-10IDEA在創(chuàng)建包時如何把包分開實現(xiàn)自動分層(方法詳解)
這篇文章主要介紹了IDEA在創(chuàng)建包時如何把包分開實現(xiàn)自動分層,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09java+vue實現(xiàn)添加單選題、多選題到題庫功能
這篇文章主要為大家詳細介紹了java+vue實現(xiàn)添加單選題、多選題到題庫功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04