英文:
Spring security StrictHttpFirewall isn't validating the headers
问题
我正在尝试添加自定义的StrictHttpFirewall,以便在标头不匹配我的模式时能够拒绝请求。
我有以下Spring安全配置:
@Configuration
@EnableWebSecurity
class SecurityConfig {
companion object {
private val HEADER_NAME_PATTERN = "^[a-z1-9_-]{1,512}$".toRegex(RegexOption.IGNORE_CASE)
private val HEADER_VALUE_BASE_PATTERN = "[\\p{IsAssigned}&&[^\\p{IsControl}]]*".toRegex()
private val HEADER_VALUE_ERROR_PATTERN = "[<>]".toRegex()
}
@Bean
fun requestRejectedHandler(): RequestRejectedHandler = ErrorResponseRequestRejectedHandler()
@Bean
fun httpFirewall(): HttpFirewall {
val firewall = StrictHttpFirewall()
val headerNames = { header: String -> HEADER_NAME_PATTERN.matches(header) }
val headerValues = { value: String ->
HEADER_VALUE_BASE_PATTERN.containsMatchIn(value).and(
!HEADER_VALUE_ERROR_PATTERN.containsMatchIn(value)
)
}
firewall.setAllowedHeaderNames(headerNames)
firewall.setAllowedHeaderValues(headerValues)
firewall.setAllowedHttpMethods(
listOf(GET.name(), POST.name(), PUT.name(), PATCH.name(), DELETE.name())
)
return firewall
}
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain =
http
.authorizeHttpRequests { it.anyRequest().permitAll() }
.sessionManagement {
it
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.disable()
}
.headers {
it
.frameOptions { option -> option.sameOrigin() }
.xssProtection(Customizer.withDefaults())
.cacheControl { cache -> cache.disable() }
.contentSecurityPolicy(Customizer.withDefaults())
}
.cors { it.disable() }
.csrf { it.disable() }
.requestCache { it.disable() }
.build()
}
但不幸的是,它不起作用,我仍然可以传递不匹配我的正则表达式的标头。我进行了长时间的调试会话,似乎请求本身(由mockMVC制作)具有所有标头,但Spring安全未检查来自请求本身的标头。有趣的是:它正在验证我在控制器方法参数级别指定为必需的标头,例如@RequestHeader(value = "my-header", required = true)。非常感谢任何帮助。
已经尝试过:
1)明确将httpFirewall设置为WebSecurity - 无法成功
2)添加自定义过滤器以调试标头,结果标头名称变得有效,但这仅因为我显式调用了request.headerNames.toList()而导致的,因为请求本身在内部使用了HttpFirewallRequest实现,该实现具有我实际预期的验证的重写方法getHeaders。
3)不仅从mockMvc而且还从RestTemplate进行请求,并仅通过postman进行请求 - 无法成功
英文:
I am trying to add my custom StrictHttpFirewall to be able to reject requests in case of headers are not matching my pattern.
I have the following configuration of Spring security:
@Configuration
@EnableWebSecurity
class SecurityConfig {
companion object {
private val HEADER_NAME_PATTERN = "^[a-z1-9_-]{1,512}$".toRegex(RegexOption.IGNORE_CASE)
private val HEADER_VALUE_BASE_PATTERN = "[\\p{IsAssigned}&&[^\\p{IsControl}]]*".toRegex()
private val HEADER_VALUE_ERROR_PATTERN = "[<>]".toRegex()
}
@Bean
fun requestRejectedHandler(): RequestRejectedHandler = ErrorResponseRequestRejectedHandler()
@Bean
fun httpFirewall(): HttpFirewall {
val firewall = StrictHttpFirewall()
val headerNames = { header: String -> HEADER_NAME_PATTERN.matches(header) }
val headerValues = { value: String ->
HEADER_VALUE_BASE_PATTERN.containsMatchIn(value).and(
!HEADER_VALUE_ERROR_PATTERN.containsMatchIn(value)
)
}
firewall.setAllowedHeaderNames(headerNames)
firewall.setAllowedHeaderValues(headerValues)
firewall.setAllowedHttpMethods(
listOf(GET.name(), POST.name(), PUT.name(), PATCH.name(), DELETE.name())
)
return firewall
}
@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain =
http
.authorizeHttpRequests { it.anyRequest().permitAll() }
.sessionManagement {
it
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.disable()
}
.headers {
it
.frameOptions { option -> option.sameOrigin() }
.xssProtection(Customizer.withDefaults())
.cacheControl { cache -> cache.disable() }
.contentSecurityPolicy(Customizer.withDefaults())
}
.cors { it.disable() }
.csrf { it.disable() }
.requestCache { it.disable() }
.build()
}
But unfortunately it's not working, I am still able to pass headers that is not match my regex.
I have a long debugging session, and it seams like request itself (made by mockMVC) having all the headers, but spring security is not checking the headers from request itself. Interesting note: it's validating the header that I specified as mandatory on my controller method parameter level - like @RequestHeader(value = "my-header", required = true). Would be appreciate for any help.
Already tried:
- Explicitly set httpFirewall to WebSecurity - no success
- Added custom filter to debug the headers, as result header names become validated, but just because I explicitly called the
request.headerNames.toList()
because the request itself is wrapped internally with HttpFirewallRequest implementation, that have overriden methodgetHeaders
with the validation that I actually expected. - Make request not only from
mockMvc
but alsoRestTemplate
and just make the request via postman - no success
答案1
得分: 0
StrictHttpFirewall
会懒惰地检查一个标头。此外,您必须注意 StrictHttpFirewall
将请求和响应都包装在 Wrapper
中。
因此,只有在 filterChain
下发出请求来访问黑名单标头之一时,StrictHttpFirewall
才会实际拒绝请求。这意味着即使您有一个黑名单标头,但在过滤器链或控制器中没有人尝试从该标头读取,除非尝试读取它,否则不会拒绝您的请求。
在引入此类保护的 PR 中,曾就此类标头检查的性能进行了讨论,因为此检查将被多次执行。
为了满足您的要求,我认为 StrictHttpFirewall
无法处理此问题。要急切地拒绝请求,您将需要编写一个位于过滤器链开头或 MVC 层的过滤器,检查是否存在这些标头并拒绝它。
通过在过滤器链中引入请求,您可以获得的一个优势是 FilterChainProxy
接受一个 RequestReject(ed)Handler
,该处理程序将能够处理 RequestReject(ed)Exception
,如果您选择抛出它。
英文:
StrictHttpFirewall
checks for a header but lazily. Also, you have to notice that StrictHttpFirewall
wraps the request and response both into Wrapper
.
With this, StrictHttpFirewall
won't actually reject the request unless down the filterChain
, a request is made to access one of the black listed headers. It means even if you have a blacklisted header, but nobody in filter chain or controller is trying to read from that header, your request won't be rejected until it is attempted to be read.
In PR where such protection was introduced, a discussion was held related to performance of such header check as this check would be executed multiple times.
To address your requirement, I don't think this can be handled by StrictHttpFirewall
. To reject a request eagerly, you will have to write a filter that sits at the beginning of filter chain, or at the mvc layer, checks for presence of such headers and rejects it.
One advantage that you can have by introducing request in filter chain is FilterChainProxy
accepts a RequestReject(ed)Handler
that will be able to handle RequestReject(ed)Exception
should you choose to throw it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论