Skip to content

Commit

Permalink
Merge pull request #30672 from sberyozkin/csrf_authentication_failure
Browse files Browse the repository at this point in the history
Avoid creating CSRF cookie if no CSRF token was created
  • Loading branch information
sberyozkin authored Feb 8, 2023
2 parents 2381d19 + dbd8554 commit 20c459c
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
17 changes: 17 additions & 0 deletions integration-tests/csrf-reactive/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-csrf-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
Expand Down Expand Up @@ -47,6 +51,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AuthenticationFailedException> {

@Override
public Response toResponse(AuthenticationFailedException exception) {
return Response.status(401).header("test-mapper", "true").build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -39,6 +40,7 @@ public class TestResource {
@GET
@Path("/csrfTokenForm")
@Produces(MediaType.TEXT_HTML)
@Authenticated
public TemplateInstance getCsrfTokenForm() {
return csrfTokenForm.instance();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
Expand Down Expand Up @@ -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());
Expand All @@ -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()) {
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
}
}

0 comments on commit 20c459c

Please sign in to comment.