Skip to content

Commit dbd8554

Browse files
committed
Avoid creating CSRF cookie if no CSRF token was created
1 parent 2381d19 commit dbd8554

File tree

6 files changed

+72
-8
lines changed

6 files changed

+72
-8
lines changed

extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,18 @@ && getCookieToken(routing, config) == null) {
179179
byte[] csrfTokenBytes = (byte[]) routing.get(CSRF_TOKEN_BYTES_KEY);
180180

181181
if (csrfTokenBytes == null) {
182-
throw new IllegalStateException(
183-
"CSRF Filter should have set the property " + CSRF_TOKEN_KEY + ", but it is null");
182+
LOG.debug("CSRF Request Filter did not set the property " + CSRF_TOKEN_BYTES_KEY
183+
+ ", no CSRF cookie will be created");
184+
return;
184185
}
185186
cookieValue = CsrfTokenUtils.signCsrfToken(csrfTokenBytes, config.tokenSignatureKey.get());
186187
} else {
187188
String csrfToken = (String) routing.get(CSRF_TOKEN_KEY);
188189

189190
if (csrfToken == null) {
190-
throw new IllegalStateException(
191-
"CSRF Filter should have set the property " + CSRF_TOKEN_KEY + ", but it is null");
191+
LOG.debug("CSRF Request Filter did not set the property " + CSRF_TOKEN_KEY
192+
+ ", no CSRF cookie will be created");
193+
return;
192194
}
193195
cookieValue = csrfToken;
194196
}

integration-tests/csrf-reactive/pom.xml

+17
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
<groupId>io.quarkus</groupId>
2020
<artifactId>quarkus-csrf-reactive</artifactId>
2121
</dependency>
22+
<dependency>
23+
<groupId>io.quarkus</groupId>
24+
<artifactId>quarkus-elytron-security-properties-file</artifactId>
25+
</dependency>
2226
<dependency>
2327
<groupId>io.quarkus</groupId>
2428
<artifactId>quarkus-junit5</artifactId>
@@ -47,6 +51,19 @@
4751
</exclusion>
4852
</exclusions>
4953
</dependency>
54+
<dependency>
55+
<groupId>io.quarkus</groupId>
56+
<artifactId>quarkus-elytron-security-properties-file-deployment</artifactId>
57+
<version>${project.version}</version>
58+
<type>pom</type>
59+
<scope>test</scope>
60+
<exclusions>
61+
<exclusion>
62+
<groupId>*</groupId>
63+
<artifactId>*</artifactId>
64+
</exclusion>
65+
</exclusions>
66+
</dependency>
5067
</dependencies>
5168

5269
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkus.it.csrf;
2+
3+
import jakarta.ws.rs.core.Response;
4+
import jakarta.ws.rs.ext.ExceptionMapper;
5+
import jakarta.ws.rs.ext.Provider;
6+
7+
import io.quarkus.security.AuthenticationFailedException;
8+
9+
@Provider
10+
public class TestExceptionMapper implements ExceptionMapper<AuthenticationFailedException> {
11+
12+
@Override
13+
public Response toResponse(AuthenticationFailedException exception) {
14+
return Response.status(401).header("test-mapper", "true").build();
15+
}
16+
17+
}

integration-tests/csrf-reactive/src/main/java/io/quarkus/it/csrf/TestResource.java

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.quarkus.csrf.reactive.runtime.CsrfTokenUtils;
2020
import io.quarkus.qute.Template;
2121
import io.quarkus.qute.TemplateInstance;
22+
import io.quarkus.security.Authenticated;
2223
import io.vertx.ext.web.RoutingContext;
2324

2425
@Path("/service")
@@ -39,6 +40,7 @@ public class TestResource {
3940
@GET
4041
@Path("/csrfTokenForm")
4142
@Produces(MediaType.TEXT_HTML)
43+
@Authenticated
4244
public TemplateInstance getCsrfTokenForm() {
4345
return csrfTokenForm.instance();
4446
}

integration-tests/csrf-reactive/src/main/resources/application.properties

+5
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ quarkus.csrf-reactive.cookie-name=csrftoken
22
quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart
33
quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
44

5+
quarkus.http.auth.basic=true
6+
quarkus.security.users.embedded.enabled=true
7+
quarkus.security.users.embedded.plain-text=true
8+
quarkus.security.users.embedded.users.alice=alice
9+
quarkus.security.users.embedded.roles.alice=admin

integration-tests/csrf-reactive/src/test/java/io/quarkus/it/csrf/CsrfReactiveTest.java

+25-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import static org.junit.jupiter.api.Assertions.assertNull;
66
import static org.junit.jupiter.api.Assertions.fail;
77

8+
import java.util.Base64;
9+
810
import org.junit.jupiter.api.Test;
911

1012
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
@@ -24,7 +26,7 @@ public class CsrfReactiveTest {
2426
@Test
2527
public void testCsrfTokenInForm() throws Exception {
2628
try (final WebClient webClient = createWebClient()) {
27-
29+
webClient.addRequestHeader("Authorization", basicAuth("alice", "alice"));
2830
HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm");
2931

3032
assertEquals("CSRF Token Form Test", htmlPage.getTitleText());
@@ -74,7 +76,7 @@ public void testCsrfTokenWithFormRead() throws Exception {
7476
@Test
7577
public void testCsrfTokenInFormButNoCookie() throws Exception {
7678
try (final WebClient webClient = createWebClient()) {
77-
79+
webClient.addRequestHeader("Authorization", basicAuth("alice", "alice"));
7880
HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm");
7981

8082
assertEquals("CSRF Token Form Test", htmlPage.getTitleText());
@@ -98,6 +100,21 @@ public void testCsrfTokenInFormButNoCookie() throws Exception {
98100
}
99101
}
100102

103+
public void testCsrfFailedAuthentication() throws Exception {
104+
try (final WebClient webClient = createWebClient()) {
105+
webClient.addRequestHeader("Authorization", basicAuth("alice", "password"));
106+
try {
107+
webClient.getPage("http://localhost:8081/service/csrfTokenForm");
108+
fail("401 status error is expected");
109+
} catch (FailingHttpStatusCodeException ex) {
110+
assertEquals(401, ex.getStatusCode());
111+
assertEquals("true", ex.getResponse().getResponseHeaderValue("test-mapper"));
112+
assertNull(webClient.getCookieManager().getCookie("csrftoken"));
113+
}
114+
webClient.getCookieManager().clearCookies();
115+
}
116+
}
117+
101118
@Test
102119
public void testCsrfTokenInMultipart() throws Exception {
103120
try (final WebClient webClient = createWebClient()) {
@@ -127,7 +144,7 @@ public void testCsrfTokenInMultipart() throws Exception {
127144
@Test
128145
public void testWrongCsrfTokenCookieValue() throws Exception {
129146
try (final WebClient webClient = createWebClient()) {
130-
147+
webClient.addRequestHeader("Authorization", basicAuth("alice", "alice"));
131148
HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm");
132149

133150
assertEquals("CSRF Token Form Test", htmlPage.getTitleText());
@@ -157,7 +174,7 @@ public void testWrongCsrfTokenCookieValue() throws Exception {
157174
@Test
158175
public void testWrongCsrfTokenFormValue() throws Exception {
159176
try (final WebClient webClient = createWebClient()) {
160-
177+
webClient.addRequestHeader("Authorization", basicAuth("alice", "alice"));
161178
HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenForm");
162179

163180
assertEquals("CSRF Token Form Test", htmlPage.getTitleText());
@@ -197,4 +214,8 @@ private WebClient createWebClient() {
197214
webClient.setCssErrorHandler(new SilentCssErrorHandler());
198215
return webClient;
199216
}
217+
218+
private String basicAuth(String user, String password) {
219+
return "Basic " + Base64.getEncoder().encodeToString((user + ":" + password).getBytes());
220+
}
200221
}

0 commit comments

Comments
 (0)