From c56d6a5d6f535fde1044f3b7bf9dc22159d06cc3 Mon Sep 17 00:00:00 2001 From: Kevin2Jordan Date: Mon, 17 Jul 2023 09:13:30 +0530 Subject: [PATCH 1/2] Suppress ArrayIndexOutOfBoundsException in XorCsrfTokenRequestAttributeHandler Closes gh-13310 --- .../web/csrf/XorCsrfTokenRequestAttributeHandler.java | 5 ++++- .../csrf/XorCsrfTokenRequestAttributeHandlerTests.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java index 0a1a42d3a3e..d5699dff2ea 100644 --- a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java +++ b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java @@ -97,7 +97,7 @@ private static String getTokenValue(String actualToken, String token) { System.arraycopy(actualBytes, randomBytesSize, xoredCsrf, 0, tokenSize); byte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf); - return Utf8.decode(csrfBytes); + return (csrfBytes != null) ? Utf8.decode(csrfBytes) : null; } private static String createXoredCsrfToken(SecureRandom secureRandom, String token) { @@ -114,6 +114,9 @@ private static String createXoredCsrfToken(SecureRandom secureRandom, String tok } private static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) { + if (csrfBytes.length < randomBytes.length) { + return null; + } int len = Math.min(randomBytes.length, csrfBytes.length); byte[] xoredCsrf = new byte[len]; System.arraycopy(csrfBytes, 0, xoredCsrf, 0, csrfBytes.length); diff --git a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java index 97e1d67cae2..acad523c74c 100644 --- a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java @@ -208,6 +208,14 @@ public void resolveCsrfTokenValueWhenHeaderAndParameterSetThenHeaderIsPreferred( assertThat(tokenValue).isEqualTo(this.token.getToken()); } + @Test + public void resolveCsrfTokenIsInvalidThenReturnsNull() { + this.request.setParameter(this.token.getParameterName(), XOR_CSRF_TOKEN_VALUE); + CsrfToken csrfToken = new DefaultCsrfToken("headerName", "paramName", "a"); + String tokenValue = this.handler.resolveCsrfTokenValue(this.request, csrfToken); + assertThat(tokenValue).isNull(); + } + private static Answer fillByteArray() { return (invocation) -> { byte[] bytes = invocation.getArgument(0); From babccba11c21598536be344dfc2e68e326606ee1 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Mon, 7 Aug 2023 15:49:52 -0600 Subject: [PATCH 2/2] Polish - Add Reactive equivalent - Update copyright Issue gh-13310 --- .../csrf/XorCsrfTokenRequestAttributeHandler.java | 2 +- .../XorServerCsrfTokenRequestAttributeHandler.java | 7 +++++-- .../XorCsrfTokenRequestAttributeHandlerTests.java | 2 +- ...rServerCsrfTokenRequestAttributeHandlerTests.java | 12 +++++++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java index d5699dff2ea..8d966331ae2 100644 --- a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java +++ b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java b/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java index 12edaf0420f..1c94b233ff4 100644 --- a/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,7 @@ private static String getTokenValue(String actualToken, String token) { System.arraycopy(actualBytes, randomBytesSize, xoredCsrf, 0, tokenSize); byte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf); - return Utf8.decode(csrfBytes); + return (csrfBytes != null) ? Utf8.decode(csrfBytes) : null; } private static String createXoredCsrfToken(SecureRandom secureRandom, String token) { @@ -105,6 +105,9 @@ private static String createXoredCsrfToken(SecureRandom secureRandom, String tok } private static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) { + if (csrfBytes.length < randomBytes.length) { + return null; + } int len = Math.min(randomBytes.length, csrfBytes.length); byte[] xoredCsrf = new byte[len]; System.arraycopy(csrfBytes, 0, xoredCsrf, 0, csrfBytes.length); diff --git a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java index acad523c74c..6f508624119 100644 --- a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java index ef5b8c0cd66..315c2533025 100644 --- a/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -182,6 +182,16 @@ public void resolveCsrfTokenValueWhenHeaderAndFormDataSetThenFormDataIsPreferred StepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete(); } + @Test + public void resolveCsrfTokenIsInvalidThenReturnsNull() { + this.exchange = MockServerWebExchange.builder(MockServerHttpRequest.post("/") + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .body(this.token.getParameterName() + "=" + XOR_CSRF_TOKEN_VALUE)).build(); + CsrfToken token = new DefaultCsrfToken("headerName", "paramName", "a"); + Mono csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, token); + assertThat(csrfToken.block()).isNull(); + } + private static Answer fillByteArray() { return (invocation) -> { byte[] bytes = invocation.getArgument(0);