Skip to content

Commit e2eed33

Browse files
committed
Add StrictHttpFirewall.allow* new lines and separators
Issue gh-11264
1 parent 5bf478e commit e2eed33

File tree

2 files changed

+141
-2
lines changed

2 files changed

+141
-2
lines changed

web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ public class StrictHttpFirewall implements HttpFirewall {
107107

108108
private static final List<String> FORBIDDEN_NULL = Collections.unmodifiableList(Arrays.asList("\0", "%00"));
109109

110-
private static final List<String> FORBIDDEN_LF = Collections.unmodifiableList(Arrays.asList("\r", "%0a", "%0A"));
110+
private static final List<String> FORBIDDEN_LF = Collections.unmodifiableList(Arrays.asList("\n", "%0a", "%0A"));
111111

112-
private static final List<String> FORBIDDEN_CR = Collections.unmodifiableList(Arrays.asList("\n", "%0d", "%0D"));
112+
private static final List<String> FORBIDDEN_CR = Collections.unmodifiableList(Arrays.asList("\r", "%0d", "%0D"));
113113

114114
private static final List<String> FORBIDDEN_LINE_SEPARATOR = Collections.unmodifiableList(Arrays.asList("\u2028"));
115115

@@ -358,6 +358,69 @@ public void setAllowUrlEncodedPercent(boolean allowUrlEncodedPercent) {
358358
}
359359
}
360360

361+
/**
362+
* Determines if a URL encoded Carriage Return is allowed in the path or not. The
363+
* default is not to allow this behavior because it is a frequent source of security
364+
* exploits.
365+
* @param allowUrlEncodedCarriageReturn if URL encoded Carriage Return is allowed in
366+
* the URL or not. Default is false.
367+
*/
368+
public void setAllowUrlEncodedCarriageReturn(boolean allowUrlEncodedCarriageReturn) {
369+
if (allowUrlEncodedCarriageReturn) {
370+
urlBlocklistsRemoveAll(FORBIDDEN_CR);
371+
}
372+
else {
373+
urlBlocklistsAddAll(FORBIDDEN_CR);
374+
}
375+
}
376+
377+
/**
378+
* Determines if a URL encoded Line Feed is allowed in the path or not. The default is
379+
* not to allow this behavior because it is a frequent source of security exploits.
380+
* @param allowUrlEncodedLineFeed if URL encoded Line Feed is allowed in the URL or
381+
* not. Default is false.
382+
*/
383+
public void setAllowUrlEncodedLineFeed(boolean allowUrlEncodedLineFeed) {
384+
if (allowUrlEncodedLineFeed) {
385+
urlBlocklistsRemoveAll(FORBIDDEN_LF);
386+
}
387+
else {
388+
urlBlocklistsAddAll(FORBIDDEN_LF);
389+
}
390+
}
391+
392+
/**
393+
* Determines if a URL encoded paragraph separator is allowed in the path or not. The
394+
* default is not to allow this behavior because it is a frequent source of security
395+
* exploits.
396+
* @param allowUrlEncodedParagraphSeparator if URL encoded paragraph separator is
397+
* allowed in the URL or not. Default is false.
398+
*/
399+
public void setAllowUrlEncodedParagraphSeparator(boolean allowUrlEncodedParagraphSeparator) {
400+
if (allowUrlEncodedParagraphSeparator) {
401+
this.decodedUrlBlocklist.removeAll(FORBIDDEN_PARAGRAPH_SEPARATOR);
402+
}
403+
else {
404+
this.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);
405+
}
406+
}
407+
408+
/**
409+
* Determines if a URL encoded line separator is allowed in the path or not. The
410+
* default is not to allow this behavior because it is a frequent source of security
411+
* exploits.
412+
* @param allowUrlEncodedLineSeparator if URL encoded line separator is allowed in the
413+
* URL or not. Default is false.
414+
*/
415+
public void setAllowUrlEncodedLineSeparator(boolean allowUrlEncodedLineSeparator) {
416+
if (allowUrlEncodedLineSeparator) {
417+
this.decodedUrlBlocklist.removeAll(FORBIDDEN_LINE_SEPARATOR);
418+
}
419+
else {
420+
this.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);
421+
}
422+
}
423+
361424
/**
362425
* <p>
363426
* Determines which header names should be allowed. The default is to reject header

web/src/test/java/org/springframework/security/web/firewall/StrictHttpFirewallTests.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,82 @@ public void getFirewalledRequestWhenServletPathContainsParagraphSeparatorThenExc
440440
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
441441
}
442442

443+
@Test
444+
public void getFirewalledRequestWhenContainsLowercaseEncodedLineFeedAndAllowedThenNoException() {
445+
this.firewall.setAllowUrlEncodedLineFeed(true);
446+
this.request.setRequestURI("/something%0a/");
447+
this.firewall.getFirewalledRequest(this.request);
448+
}
449+
450+
@Test
451+
public void getFirewalledRequestWhenContainsUppercaseEncodedLineFeedAndAllowedThenNoException() {
452+
this.firewall.setAllowUrlEncodedLineFeed(true);
453+
this.request.setRequestURI("/something%0A/");
454+
this.firewall.getFirewalledRequest(this.request);
455+
}
456+
457+
@Test
458+
public void getFirewalledRequestWhenContainsLineFeedAndAllowedThenException() {
459+
this.firewall.setAllowUrlEncodedLineFeed(true);
460+
this.request.setRequestURI("/something\n/");
461+
// Expected an error because the line feed is decoded in an encoded part of the
462+
// URL
463+
assertThatExceptionOfType(RequestRejectedException.class)
464+
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
465+
}
466+
467+
@Test
468+
public void getFirewalledRequestWhenServletPathContainsLineFeedAndAllowedThenNoException() {
469+
this.firewall.setAllowUrlEncodedLineFeed(true);
470+
this.request.setServletPath("/something\n/");
471+
this.firewall.getFirewalledRequest(this.request);
472+
}
473+
474+
@Test
475+
public void getFirewalledRequestWhenContainsLowercaseEncodedCarriageReturnAndAllowedThenNoException() {
476+
this.firewall.setAllowUrlEncodedCarriageReturn(true);
477+
this.request.setRequestURI("/something%0d/");
478+
this.firewall.getFirewalledRequest(this.request);
479+
}
480+
481+
@Test
482+
public void getFirewalledRequestWhenContainsUppercaseEncodedCarriageReturnAndAllowedThenNoException() {
483+
this.firewall.setAllowUrlEncodedCarriageReturn(true);
484+
this.request.setRequestURI("/something%0D/");
485+
this.firewall.getFirewalledRequest(this.request);
486+
}
487+
488+
@Test
489+
public void getFirewalledRequestWhenContainsCarriageReturnAndAllowedThenNoException() {
490+
this.firewall.setAllowUrlEncodedCarriageReturn(true);
491+
this.request.setRequestURI("/something\r/");
492+
// Expected an error because the carriage return is decoded in an encoded part of
493+
// the URL
494+
assertThatExceptionOfType(RequestRejectedException.class)
495+
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
496+
}
497+
498+
@Test
499+
public void getFirewalledRequestWhenServletPathContainsCarriageReturnAndAllowedThenNoException() {
500+
this.firewall.setAllowUrlEncodedCarriageReturn(true);
501+
this.request.setServletPath("/something\r/");
502+
this.firewall.getFirewalledRequest(this.request);
503+
}
504+
505+
@Test
506+
public void getFirewalledRequestWhenServletPathContainsLineSeparatorAndAllowedThenNoException() {
507+
this.firewall.setAllowUrlEncodedLineSeparator(true);
508+
this.request.setServletPath("/something\u2028/");
509+
this.firewall.getFirewalledRequest(this.request);
510+
}
511+
512+
@Test
513+
public void getFirewalledRequestWhenServletPathContainsParagraphSeparatorAndAllowedThenNoException() {
514+
this.firewall.setAllowUrlEncodedParagraphSeparator(true);
515+
this.request.setServletPath("/something\u2029/");
516+
this.firewall.getFirewalledRequest(this.request);
517+
}
518+
443519
/**
444520
* On WebSphere 8.5 a URL like /context-root/a/b;%2f1/c can bypass a rule on /a/b/c
445521
* because the pathInfo is /a/b;/1/c which ends up being /a/b/1/c while Spring MVC

0 commit comments

Comments
 (0)