Skip to content

Commit

Permalink
Do not use CSRF cookie as the next token value
Browse files Browse the repository at this point in the history
(cherry picked from commit 0e4097c)
  • Loading branch information
sberyozkin authored and gsmet committed Dec 18, 2023
1 parent df70f99 commit f9870f6
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi

String cookieToken = getCookieToken(routing, config);
if (cookieToken != null) {
routing.put(CSRF_TOKEN_KEY, cookieToken);

try {
int cookieTokenSize = Base64.getUrlDecoder().decode(cookieToken).length;
// HMAC SHA256 output is 32 bytes long
Expand Down Expand Up @@ -98,10 +96,10 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi
// unsafe HTTP method, token is required

// Check the header first
String csrfTokenInHeader = requestContext.getHeaderString(config.tokenHeaderName);
if (csrfTokenInHeader != null) {
String csrfTokenHeaderParam = requestContext.getHeaderString(config.tokenHeaderName);
if (csrfTokenHeaderParam != null) {
LOG.debugf("CSRF token found in the token header");
verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenInHeader);
verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenHeaderParam);
return;
}

Expand All @@ -128,9 +126,9 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi

ResteasyReactiveRequestContext rrContext = (ResteasyReactiveRequestContext) requestContext
.getServerRequestContext();
String csrfToken = (String) rrContext.getFormParameter(config.formFieldName, true, false);
String csrfTokenFormParam = (String) rrContext.getFormParameter(config.formFieldName, true, false);
LOG.debugf("CSRF token found in the form parameter");
verifyCsrfToken(requestContext, routing, config, cookieToken, csrfToken);
verifyCsrfToken(requestContext, routing, config, cookieToken, csrfTokenFormParam);
return;

} else if (cookieToken == null) {
Expand Down Expand Up @@ -159,6 +157,7 @@ private void verifyCsrfToken(ResteasyReactiveContainerRequestContext requestCont
requestContext.abortWith(badClientRequest());
return;
} else {
routing.put(CSRF_TOKEN_KEY, csrfToken);
routing.put(CSRF_TOKEN_VERIFIED, true);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public class TestResource {
@Inject
Template csrfTokenForm;

@Inject
Template csrfTokenFirstForm;

@Inject
Template csrfTokenSecondForm;

@Inject
Template csrfTokenHeader;

Expand All @@ -49,6 +55,14 @@ public TemplateInstance getCsrfTokenForm() {
return csrfTokenForm.instance();
}

@GET
@Path("/csrfTokenFirstForm")
@Produces(MediaType.TEXT_HTML)
@Authenticated
public TemplateInstance getCsrfTokenFirstForm() {
return csrfTokenFirstForm.instance();
}

@GET
@Path("/csrfTokenWithFormRead")
@Produces(MediaType.TEXT_HTML)
Expand All @@ -71,6 +85,22 @@ public String postCsrfTokenForm(@FormParam("name") String name, @HeaderParam("X-
return name + ":" + routingContext.get("csrf_token_verified", false) + ":tokenHeaderIsSet=" + (csrfHeader != null);
}

@POST
@Path("/csrfTokenFirstForm")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_HTML)
public TemplateInstance postCsrfTokenFirstForm() {
return csrfTokenSecondForm.instance();
}

@POST
@Path("/csrfTokenSecondForm")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
public String postCsrfTokenSecondForm(@FormParam("name") String name, @HeaderParam("X-CSRF-TOKEN") String csrfHeader) {
return name + ":" + routingContext.get("csrf_token_verified", false) + ":tokenHeaderIsSet=" + (csrfHeader != null);
}

@POST
@Path("/csrfTokenWithFormRead")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
quarkus.csrf-reactive.cookie-name=csrftoken
quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart,/service/csrfTokenWithHeader
quarkus.csrf-reactive.create-token-path=/service/csrfTokenForm,/service/csrfTokenFirstForm,/service/csrfTokenSecondForm,/service/csrfTokenWithFormRead,/service/csrfTokenMultipart,/service/csrfTokenWithHeader
quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow

quarkus.http.auth.basic=true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CSRF Token First Form Test</title>
</head>
<body>
<h1>CSRF Test</h1>

<form action="/service/csrfTokenFirstForm" method="post">
<input type="hidden" name="{inject:csrf.parameterName}" value="{inject:csrf.token}" />

<p>Your Name: <input type="text" name="name" /></p>
<p><input type="submit" name="submit"/></p>
</form>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CSRF Token Second Form Test</title>
</head>
<body>
<h1>CSRF Test</h1>

<form action="/service/csrfTokenSecondForm" method="post">
<input type="hidden" name="{inject:csrf.parameterName}" value="{inject:csrf.token}" />

<p>Your Name: <input type="text" name="name" /></p>
<p><input type="submit" name="submit"/></p>
</form>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,36 @@ public void testCsrfTokenInForm() throws Exception {
}
}

@Test
public void testCsrfTokenTwoForms() throws Exception {
try (final WebClient webClient = createWebClient()) {
webClient.addRequestHeader("Authorization", basicAuth("alice", "alice"));
HtmlPage htmlPage = webClient.getPage("http://localhost:8081/service/csrfTokenFirstForm");

assertEquals("CSRF Token First Form Test", htmlPage.getTitleText());

HtmlForm loginForm = htmlPage.getForms().get(0);

loginForm.getInputByName("name").setValueAttribute("alice");

assertNotNull(webClient.getCookieManager().getCookie("csrftoken"));

htmlPage = loginForm.getInputByName("submit").click();

assertEquals("CSRF Token Second Form Test", htmlPage.getTitleText());

loginForm = htmlPage.getForms().get(0);

loginForm.getInputByName("name").setValueAttribute("alice");

TextPage textPage = loginForm.getInputByName("submit").click();
assertNotNull(webClient.getCookieManager().getCookie("csrftoken"));
assertEquals("alice:true:tokenHeaderIsSet=false", textPage.getContent());

webClient.getCookieManager().clearCookies();
}
}

@Test
public void testCsrfTokenWithFormRead() throws Exception {
try (final WebClient webClient = createWebClient()) {
Expand Down

0 comments on commit f9870f6

Please sign in to comment.