docs
reference
tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.
Quarkus REST provides built-in Cross-Site Request Forgery (CSRF) protection for REST endpoints. The CSRF filter validates tokens on state-changing requests (POST, PUT, DELETE, PATCH) to prevent unauthorized cross-site requests.
import io.quarkus.csrf.reactive.runtime.RestCsrfConfig;Runtime configuration for REST CSRF protection.
/**
* CSRF protection configuration for REST endpoints
* Configured via quarkus.rest-csrf.* properties (implied prefix)
*/
public class RestCsrfConfig {
/**
* Form field name containing the CSRF token
* Default: "csrf-token"
*/
String formFieldName = "csrf-token";
/**
* HTTP header name for CSRF token
* Default: "X-CSRF-TOKEN"
*/
String tokenHeaderName = "X-CSRF-TOKEN";
/**
* Cookie name for CSRF token
* Default: "csrf-token"
*/
String cookieName = "csrf-token";
/**
* CSRF cookie maximum age
* Default: 2H (2 hours)
*/
Duration cookieMaxAge = Duration.ofHours(2);
/**
* CSRF cookie path
* Default: "/"
*/
String cookiePath = "/";
/**
* CSRF cookie domain
* Optional: If not set, uses request domain
*/
Optional<String> cookieDomain;
/**
* Force secure flag on CSRF cookie (HTTPS only)
* Default: false
*/
boolean cookieForceSecure = false;
/**
* Set HttpOnly flag on CSRF cookie
* Default: true
*/
boolean cookieHttpOnly = true;
/**
* Paths that trigger CSRF token creation (GET requests)
* Optional: If not set, token is created on any GET request
*/
Optional<Set<String>> createTokenPath;
/**
* Size of random CSRF token in bytes
* Default: 16 bytes
*/
int tokenSize = 16;
/**
* HMAC signature key for token signing (min 32 characters)
* Optional: If not set, tokens are not signed
*/
Optional<String> tokenSignatureKey;
/**
* Enable CSRF token verification
* Default: true
*/
boolean verifyToken = true;
/**
* Require application/x-www-form-urlencoded content type for form submissions
* Default: true
*/
boolean requireFormUrlEncoded = true;
}Configure CSRF protection via application.properties. The prefix quarkus.rest-csrf.* is implied for these properties:
# Form field name
quarkus.rest-csrf.form-field-name=csrf-token
# Token header name
quarkus.rest-csrf.token-header-name=X-CSRF-TOKEN
# Cookie configuration
quarkus.rest-csrf.cookie-name=csrf-token
quarkus.rest-csrf.cookie-max-age=2h
quarkus.rest-csrf.cookie-path=/
quarkus.rest-csrf.cookie-domain=example.com
quarkus.rest-csrf.cookie-force-secure=false
quarkus.rest-csrf.cookie-http-only=true
# Token creation paths (comma-separated)
quarkus.rest-csrf.create-token-path=/form,/login
# Token configuration
quarkus.rest-csrf.token-size=16
quarkus.rest-csrf.token-signature-key=your-secret-key-minimum-32-characters-long
# Verification settings
quarkus.rest-csrf.verify-token=true
quarkus.rest-csrf.require-form-url-encoded=trueEnable CSRF protection by adding the extension and configuring it:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-csrf</artifactId>
</dependency>Configuration:
# Enable CSRF protection with default settings
quarkus.rest-csrf.verify-token=trueimport io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;
@Path("/form")
public class FormResource {
@Inject
Template form;
@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance showForm(@CookieParam("csrf-token") String csrfToken) {
// CSRF token is automatically created and set in cookie
return form.data("csrfToken", csrfToken);
}
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response submitForm(
@FormParam("name") String name,
@FormParam("csrf-token") String csrfToken
) {
// CSRF token is automatically validated
return Response.ok("Form submitted successfully").build();
}
}HTML template (form.html):
<!DOCTYPE html>
<html>
<body>
<form method="POST" action="/form">
<input type="hidden" name="csrf-token" value="{csrfToken}">
<input type="text" name="name" placeholder="Your name">
<button type="submit">Submit</button>
</form>
</body>
</html>@Path("/api")
public class ApiResource {
@POST
@Path("/action")
public Response performAction(ActionRequest request) {
// Client must send CSRF token in X-CSRF-TOKEN header
return Response.ok("Action completed").build();
}
}JavaScript client:
// Get CSRF token from cookie
const csrfToken = document.cookie
.split('; ')
.find(row => row.startsWith('csrf-token='))
?.split('=')[1];
// Send token in header
fetch('/api/action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({ action: 'delete' })
});For enhanced security, use HMAC-signed tokens:
# Set signature key (minimum 32 characters)
quarkus.rest-csrf.token-signature-key=your-very-long-secret-key-at-least-32-characters-or-moreSigned tokens prevent token tampering and forgery.
Create CSRF tokens only on specific pages:
# Only create tokens on these GET endpoints
quarkus.rest-csrf.create-token-path=/login,/dashboard,/formsThis limits token creation to reduce overhead for public read-only endpoints.
# Force HTTPS-only cookies in production
%prod.quarkus.rest-csrf.cookie-force-secure=true
# Set specific cookie domain
%prod.quarkus.rest-csrf.cookie-domain=example.com
# Use signed tokens
%prod.quarkus.rest-csrf.token-signature-key=${CSRF_SECRET_KEY}
# Shorter max age
%prod.quarkus.rest-csrf.cookie-max-age=30m# Disable CSRF in development (use with caution)
%dev.quarkus.rest-csrf.verify-token=false
# Or use relaxed settings
%dev.quarkus.rest-csrf.cookie-force-secure=false
%dev.quarkus.rest-csrf.cookie-http-only=falseFor multi-application deployments:
# Scope CSRF cookie to specific application path
quarkus.rest-csrf.cookie-path=/appFor compatibility with frontend frameworks:
# Use framework-specific header name
quarkus.rest-csrf.token-header-name=X-XSRF-TOKEN
quarkus.rest-csrf.cookie-name=XSRF-TOKENThis is compatible with Angular's built-in CSRF support.
For high-security requirements:
# Increase token entropy (32 bytes = 256 bits)
quarkus.rest-csrf.token-size=32CSRF protection works with multipart forms:
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(
@RestForm("file") FileUpload file,
@RestForm("csrf-token") String csrfToken
) {
// CSRF token is automatically validated
return Response.ok("File uploaded").build();
}HTML form:
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="hidden" name="csrf-token" value="{csrfToken}">
<input type="file" name="file">
<button type="submit">Upload</button>
</form># Disable CSRF for specific profiles
%test.quarkus.rest-csrf.verify-token=false
# Enable only in production
%prod.quarkus.rest-csrf.verify-token=trueFor endpoints that should not require CSRF tokens (like public APIs), use path-based exclusion via HTTP firewall configuration or custom filters.
%prod.quarkus.rest-csrf.cookie-force-secure=trueCSRF tokens in cookies should only be transmitted over HTTPS to prevent interception.
quarkus.rest-csrf.token-signature-key=${CSRF_SECRET_KEY}Store the signature key in environment variables or secret management systems, never in source code.
# Short-lived sessions
quarkus.rest-csrf.cookie-max-age=30m
# Long-lived sessions
quarkus.rest-csrf.cookie-max-age=24hBalance security and user experience based on your application's session duration.
# Application-specific path
quarkus.rest-csrf.cookie-path=/app
# Subdomain scope
quarkus.rest-csrf.cookie-domain=.example.comPrevent cookie leakage to unrelated applications on the same domain.
quarkus.rest-csrf.cookie-http-only=trueThis prevents JavaScript access to CSRF tokens, reducing XSS risk (though CSRF tokens should still be accessible to your application's JavaScript for AJAX requests).
Always test CSRF protection in staging environments before deploying to production:
@QuarkusTest
public class CsrfTest {
@Test
public void testCsrfProtection() {
// Request without CSRF token should fail
given()
.contentType(ContentType.URLENC)
.formParam("data", "value")
.when()
.post("/form")
.then()
.statusCode(403);
// Request with valid token should succeed
String token = given()
.get("/form")
.cookie("csrf-token");
given()
.contentType(ContentType.URLENC)
.formParam("data", "value")
.formParam("csrf-token", token)
.cookie("csrf-token", token)
.when()
.post("/form")
.then()
.statusCode(200);
}
}The default configuration uses the double-submit cookie pattern:
// React/Vue/Angular example
async function makeSecureRequest(url, data) {
const csrfToken = getCookie('csrf-token');
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
credentials: 'include', // Include cookies
body: JSON.stringify(data)
});
return response.json();
}
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}Angular:
// Angular automatically handles XSRF tokens
// Configure to match Quarkus settings:
// quarkus.rest-csrf.token-header-name=X-XSRF-TOKEN
// quarkus.rest-csrf.cookie-name=XSRF-TOKEN
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
@NgModule({
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN'
})
]
})
export class AppModule { }cookie-max-ageCheck that:
cookie-force-secure=trueEnsure:
verify-token=true (enables CSRF filter)create-token-path includes the endpoint (if configured)Adjust cookie-max-age based on session duration:
# Longer session duration
quarkus.rest-csrf.cookie-max-age=8h