-
Notifications
You must be signed in to change notification settings - Fork 0
Home
A lightweight set of escaping routines for cross-site scripting (XSS) in SoftLeader web applications.
預設開啟 spring security 的 xss protection, 會在每次 response 的 header 中加上下列 header, 讓有支援的瀏覽器來協助防範 xss 攻擊
X-XSS-Protection: 1; mode=block
see X-XSS-Protection
若專案需要客製設定, 可參考下述程式碼
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends MoreWebSecurityConfiguration {
...
@Override
protected void headers(HeadersConfigurer<HttpSecurity> headers) {
super.headers(headers);
// headers.xssProtection().disable(); // 關掉
headers.xssProtection().block(false); // 不設定 mode=block
}
}
註冊 XSSProtectionFilter
針對 HttpServletRequest
提供了跳脫 xss 的裝飾實作
在專案的 WebApplicationInitializer
加上 filter 即完成設定
public class WebApplicationInitializer extends ProfileAnnotationConfigDispatcherServletInitializer {
...
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter(),
new HtmlXSSProtectionFilter()
};
}
}
核心提供了 HtmlXSSProtectionFilter
的實作, 是使用 Spring 的 HtmlUtils.htmlEscape(String) 來跳脫
如果專案想客製 , 可以改使用上層的 XSSProtectionFilter
來自行實作跳脫邏輯
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter(),
new XSSProtectionFilter(original -> {
...customize your escaping logic
return escaped;
})
};
}
或接續核心的 HtmlEscaper
繼續跳脫
new XSSProtectionFilter(new HtmlEscaper().andThen(original -> {
...customize more escaping logic
return escaped;
}))
加上 filter 後, 任何從 HttpServletRequest
呼叫下述 method 取回的值, 都會自動跳脫:
String getParameter(String)
Map<String, String[]> getParameterMap()
String[] getParameterValues(String)
@RestController
@RequestMapping("/some-path")
public class SomeController {
public void escape(HttpServletRequest request) {
String escaped = request.getParameter(..);
...
}
}
若有需求可以參考下述程式碼, 取得尚未跳脫前的值
@RestController
@RequestMapping("/some-path")
public class SomeController {
public void escape(HttpServletRequest request) {
String beforeEscape =
((XSSHttpServletRequest) request)
.getNativeRequest(HttpServletRequest.class)
.map(nativeRequest -> nativeRequest.getParameter(...))
.orElseThrow(/** handle if not available **/);
...
}
}
也支援 spring 提供的 @RequestParam
取值方式
@RestController
@RequestMapping("/some-path")
public class SomeController {
public void escape(@RequestParam(...) String escaped) {
...
}
}
注意: 使用
@RequestParam
將無法再拿到跳脫前的值,且如果宣告的是物件, spring 會以跳脫後的值進行轉換
註冊 XSSProtectionModule
會針對所有 String
欄位在 deserialize 時自動跳脫
public class WebMvcConfig extends WebMvcConfiguration {
...
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
...
argumentResolvers.add(new SpecificationArgumentResolver(new Converter(objectMapper())));
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
...
converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
}
@Bean
public ObjectMapper objectMapper() {
return Jackson2ObjectMapperBuilder
.json()
.modules(new HtmlXSSProtectionModule())
.build();
}
}
如果專案想客製, 可以改使用上層的 XSSProtectionModule
來自行實作客製邏輯
new XSSProtectionModule(original -> {
...customize your escaping logic
return escaped;
});
如果專案在 spring 中已經定義了一個 global 的 object mapper, 建議將 XSSProtectionModule
的跳脫範圍限制在 request 進 controller 轉換使用, 以避在任何地方只要用到 object mapper 來轉換時就會跳脫, 讓範圍太大較難以控制
public class WebMvcConfig extends WebMvcConfiguration {
...
@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
...
argumentResolvers.add(new SpecificationArgumentResolver(new Converter(xssObjectMapper())));
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
...
converters.add(new MappingJackson2HttpMessageConverter(xssObjectMapper()));
}
// 將原本的 mapper 設定成 @Primary 讓一般程式使用
@Primary
@Bean
public ObjectMapper objectMapper() {
return new MyObjectMapper();
}
// 跟處理 request 有關係的程式才加上 XSSProtectionModule
@Bean
public ObjectMapper xssObjectMapper() {
MyObjectMapper mapper = new MyObjectMapper();
mapper.registerModule(new HtmlXSSProtectionModule());
return mapper;
}
}
如果想看到每次跳脫前後的值, 則把下述 class 的 log level 設在 debug
tw.com.softleader.security.xss.http.XSSHttpServletRequest
tw.com.softleader.security.xss.json.jackson.XSSStringDeserializer
核心提供的實作預設都是使用 Spring 的 HtmlUtils.htmlEscape(String) 來跳脫,如果專案想要調整 (如改成 OWASP),則可以參考以下範例:
- pom 加上 owasp 依賴
<dependency>
<groupId>tw.com.softleader</groupId>
<artifactId>softleader-security-xss</artifactId>
<version>1.0.4-SNAPSHOT</version>
</dependency>
- pom 加上 owasp 依賴
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.2.1</version>
</dependency>
- xss filter 改成使用
org.owasp.encoder.Encode.forHtml(String)
跳脫
public class WebApplicationInitializer extends ProfileAnnotationConfigDispatcherServletInitializer {
@Override
protected Filter[] getServletFilters() {
return new Filter[]{new OpenEntityManagerInViewFilter(), new XSSProtectionFilter(Encode::forHtml)};
}
// ...
}
- xss module 也是改使用
Encode.forHtml(String)
跳脫
@Configuration
public class SomeConfig {
...
@Bean
public ObjectMapper xssObjectMapper() {
MyObjectMapper mapper = new MyObjectMapper();
mapper.registerModule(new XSSProtectionModule(Encode::forHtml));
return mapper;
}
}