From dbd85540b750137129f95208d61584e0b8beaa38 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 25 Jan 2023 17:57:03 +0000 Subject: [PATCH] Avoid creating CSRF cookie if no CSRF token was created --- .../CsrfRequestResponseReactiveFilter.java | 10 ++++--- integration-tests/csrf-reactive/pom.xml | 17 +++++++++++ .../quarkus/it/csrf/TestExceptionMapper.java | 17 +++++++++++ .../java/io/quarkus/it/csrf/TestResource.java | 2 ++ .../src/main/resources/application.properties | 5 ++++ .../io/quarkus/it/csrf/CsrfReactiveTest.java | 29 ++++++++++++++++--- 6 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestExceptionMapper.java diff --git a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java index 0102266094961..8fcba71e714b4 100644 --- a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java +++ b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java @@ -179,16 +179,18 @@ && getCookieToken(routing, config) == null) { byte[] csrfTokenBytes = (byte[]) routing.get(CSRF_TOKEN_BYTES_KEY); if (csrfTokenBytes == null) { - throw new IllegalStateException( - "CSRF Filter should have set the property " + CSRF_TOKEN_KEY + ", but it is null"); + LOG.debug("CSRF Request Filter did not set the property " + CSRF_TOKEN_BYTES_KEY + + ", no CSRF cookie will be created"); + return; } cookieValue = CsrfTokenUtils.signCsrfToken(csrfTokenBytes, config.tokenSignatureKey.get()); } else { String csrfToken = (String) routing.get(CSRF_TOKEN_KEY); if (csrfToken == null) { - throw new IllegalStateException( - "CSRF Filter should have set the property " + CSRF_TOKEN_KEY + ", but it is null"); + LOG.debug("CSRF Request Filter did not set the property " + CSRF_TOKEN_KEY + + ", no CSRF cookie will be created"); + return; } cookieValue = csrfToken; } diff --git a/integration-tests/csrf-reactive/pom.xml b/integration-tests/csrf-reactive/pom.xml index d97c3794e5e3e..9c862c7798fa6 100644 --- a/integration-tests/csrf-reactive/pom.xml +++ b/integration-tests/csrf-reactive/pom.xml @@ -19,6 +19,10 @@ io.quarkus quarkus-csrf-reactive + + io.quarkus + quarkus-elytron-security-properties-file + io.quarkus quarkus-junit5 @@ -47,6 +51,19 @@ + + io.quarkus + quarkus-elytron-security-properties-file-deployment + ${project.version} + pom + test + + + * + * + + + diff --git a/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestExceptionMapper.java b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestExceptionMapper.java new file mode 100644 index 0000000000000..85384ccb2ffc6 --- /dev/null +++ b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestExceptionMapper.java @@ -0,0 +1,17 @@ +package io.quarkus.it.csrf; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +import io.quarkus.security.AuthenticationFailedException; + +@Provider +public class TestExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(AuthenticationFailedException exception) { + return Response.status(401).header("test-mapper", "true").build(); + } + +} diff --git a/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java index c509b97cfe6b5..e4e28dc289a8f 100644 --- a/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java +++ b/integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java @@ -19,6 +19,7 @@ import io.quarkus.csrf.reactive.runtime.CsrfTokenUtils; import io.quarkus.qute.Template; import io.quarkus.qute.TemplateInstance; +import io.quarkus.security.Authenticated; import io.vertx.ext.web.RoutingContext; @Path("/service") @@ -39,6 +40,7 @@ public class TestResource { @GET @Path("/csrfTokenForm") @Produces(MediaType.TEXT_HTML) + @Authenticated public TemplateInstance getCsrfTokenForm() { return csrfTokenForm.instance(); } diff --git a/integration-tests/csrf-reactive/src/main/resources/application.properties b/integration-tests/csrf-reactive/src/main/resources/application.properties index add9b0a990af0..bf34b8f60188c 100644 --- a/integration-tests/csrf-reactive/src/main/resources/application.properties +++ b/integration-tests/csrf-reactive/src/main/resources/application.properties @@ -2,3 +2,8 @@ quarkus.csrf-reactive.cookie-name=csrftoken quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow +quarkus.http.auth.basic=true +quarkus.security.users.embedded.enabled=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.alice=alice +quarkus.security.users.embedded.roles.alice=admin \ No newline at end of file diff --git a/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java b/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java index 9c0302b75f8da..69f4e06ab7573 100644 --- a/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java +++ b/integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; +import java.util.Base64; + import org.junit.jupiter.api.Test; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; @@ -24,7 +26,7 @@ public class CsrfReactiveTest { @Test public void testCsrfTokenInForm() throws Exception { try (final WebClient webClient = createWebClient()) { - + webClient.addRequestHeader("Authorization", basicAuth("alice", "alice")); HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm"); assertEquals("CSRF Token Form Test", htmlPage.getTitleText()); @@ -74,7 +76,7 @@ public void testCsrfTokenWithFormRead() throws Exception { @Test public void testCsrfTokenInFormButNoCookie() throws Exception { try (final WebClient webClient = createWebClient()) { - + webClient.addRequestHeader("Authorization", basicAuth("alice", "alice")); HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm"); assertEquals("CSRF Token Form Test", htmlPage.getTitleText()); @@ -98,6 +100,21 @@ public void testCsrfTokenInFormButNoCookie() throws Exception { } } + public void testCsrfFailedAuthentication() throws Exception { + try (final WebClient webClient = createWebClient()) { + webClient.addRequestHeader("Authorization", basicAuth("alice", "password")); + try { + webClient.getPage("http://localhost:8081/service/csrfTokenForm"); + fail("401 status error is expected"); + } catch (FailingHttpStatusCodeException ex) { + assertEquals(401, ex.getStatusCode()); + assertEquals("true", ex.getResponse().getResponseHeaderValue("test-mapper")); + assertNull(webClient.getCookieManager().getCookie("csrftoken")); + } + webClient.getCookieManager().clearCookies(); + } + } + @Test public void testCsrfTokenInMultipart() throws Exception { try (final WebClient webClient = createWebClient()) { @@ -127,7 +144,7 @@ public void testCsrfTokenInMultipart() throws Exception { @Test public void testWrongCsrfTokenCookieValue() throws Exception { try (final WebClient webClient = createWebClient()) { - + webClient.addRequestHeader("Authorization", basicAuth("alice", "alice")); HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm"); assertEquals("CSRF Token Form Test", htmlPage.getTitleText()); @@ -157,7 +174,7 @@ public void testWrongCsrfTokenCookieValue() throws Exception { @Test public void testWrongCsrfTokenFormValue() throws Exception { try (final WebClient webClient = createWebClient()) { - + webClient.addRequestHeader("Authorization", basicAuth("alice", "alice")); HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm"); assertEquals("CSRF Token Form Test", htmlPage.getTitleText()); @@ -197,4 +214,8 @@ private WebClient createWebClient() { webClient.setCssErrorHandler(new SilentCssErrorHandler()); return webClient; } + + private String basicAuth(String user, String password) { + return "Basic " + Base64.getEncoder().encodeToString((user + ":" + password).getBytes()); + } }