diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc index fb1653478b..a5781e692d 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc @@ -131,3 +131,104 @@ See https://jira.spring.io/browse/SPR-16851[SPR_16851] for an issue requesting t If you must allow any HTTP method (not recommended), you can use `StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)`. This will disable validation of the HTTP method entirely. + +`StrictHttpFirewall` also checks header names and values and parameter names. +It requires that each character have a defined code point and not be a control character. + +This requirement can be relaxed or adjusted as necessary using the following methods: + +* `StrictHttpFirewall#setAllowedHeaderNames(Predicate)` +* `StrictHttpFirewall#setAllowedHeaderValues(Predicate)` +* `StrictHttpFirewall#setAllowedParameterNames(Predicate)` + +NOTE: Also, parameter values can be controlled with `setAllowedParameterValues(Predicate)`. + +For example, to switch off this check, you can wire your `StrictHttpFirewall` with `Predicate` s that always return `true`, like so: + +.Allow Any Header Name, Header Value, and Parameter Name +==== +.Java +[source,java,role="primary"] +---- +@Bean +public StrictHttpFirewall httpFirewall() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + firewall.setAllowedHeaderNames((header) -> true); + firewall.setAllowedHeaderValues((header) -> true); + firewall.setAllowedParameterNames((parameter) -> true); + return firewall; +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun httpFirewall(): StrictHttpFirewall { + val firewall = StrictHttpFirewall() + firewall.setAllowedHeaderNames { true } + firewall.setAllowedHeaderValues { true } + firewall.setAllowedParameterNames { true } + return firewall +} +---- +==== + +Or, there might be a specific value that you need to allow. + +For example, iPhone Xʀ uses a `User-Agent` that includes a character not in the ISO-8859-1 charset. +Due to this fact, some application servers will parse this value into two separate characters, the latter being an undefined character. + +You can address this with the `setAllowedHeaderValues` method, as you can see below: + +.Allow Certain User Agents +==== +.Java +[source,java,role="primary"] +---- +@Bean +public StrictHttpFirewall httpFirewall() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + Pattern allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*"); + Pattern userAgent = ...; + firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches()); + return firewall; +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun httpFirewall(): StrictHttpFirewall { + val firewall = StrictHttpFirewall() + val allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*") + val userAgent = Pattern.compile(...) + firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() } + return firewall +} +---- +==== + +In the case of header values, you may instead consider parsing them as UTF-8 at verification time like so: + +.Parse Headers As UTF-8 +==== +.Java +[source,java,role="primary"] +---- +firewall.setAllowedHeaderValues((header) -> { + String parsed = new String(header.getBytes(ISO_8859_1), UTF_8); + return allowed.matcher(parsed).matches(); +}); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +firewall.setAllowedHeaderValues { + val parsed = String(header.getBytes(ISO_8859_1), UTF_8) + return allowed.matcher(parsed).matches() +} +---- +====