Cross-Site Request Forgery Prevention

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they are currently authenticated.

Quarkus Security provides a CSRF prevention feature which consists of a RESTEasy Reactive server filter which creates and verifies CSRF tokens in 'application/x-www-form-urlencoded' and 'multipart/form-data' forms and a Qute HTML form parameter provider which supports the injection of CSRF tokens in Qute templates.

创建项目

首先,我们需要一个新的项目。使用以下命令创建一个新的项目:

CLI
quarkus create app org.acme:security-csrf-prevention \
    --stream=2.13 \
    --extension='csrf-reactive' \
    --no-code
cd security-csrf-prevention

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

关于如何安装并使用Quarkus CLI的更多信息,请参考Quarkus CLI指南

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.13.9.Final:create \
    -DplatformVersion=2.13.9.Final \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-csrf-prevention \
    -Dextensions='csrf-reactive' \
    -DnoCode
cd security-csrf-prevention

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

This command generates a project which imports the csrf-reactive extension.

If you already have your Quarkus project configured, you can add the csrf-reactive extension to your project by running the following command in your project base directory:

CLI
quarkus extension add 'csrf-reactive'
Maven
./mvnw quarkus:add-extension -Dextensions='csrf-reactive'
Gradle
./gradlew addExtension --extensions='csrf-reactive'

这将在您的构建文件中添加以下内容:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-csrf-reactive</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-csrf-reactive")

Next, let’s add a csrfToken.html Qute template producing an HTML form in the src/main/resources/templates folder:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>User Name Input</title>
</head>
<body>
    <h1>User Name Input</h1>

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

    	<p>Your Name: <input type="text" name="name" /></p>
    	<p><input type="submit" name="submit"/></p>
    </form>
</body>
</html>
1 This expression is used to inject a CSRF token into a hidden form field. This token will be verified by the CSRF filter against a CSRF cookie.

Now let’s create a resource class which returns an HTML form and handles form POST requests:

package io.quarkus.it.csrf;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;

@Path("/service")
public class UserNameResource {

    @Inject
    Template csrfToken; (1)

    @GET
    @Path("/csrfTokenForm")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance getCsrfTokenForm() {
        return csrfToken.instance(); (2)
    }

    @POST
    @Path("/csrfTokenForm")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.TEXT_PLAIN)
    public String postCsrfTokenForm(@FormParam("name") String userName) {
        return userName; (3)
    }
}
1 Inject the csrfToken.html as a Template.
2 Return the HTML form with a hidden form field containing a CSRF token created by the CSRF filter.
3 Handle the form POST request, this method can only be invoked if the CSRF filter has successfully verified the token.

The form POST request will fail with HTTP status 400 if the filter finds the hidden CSRF form field is missing, the CSRF cookie is missing, or if the CSRF form field and CSRF cookie values do not match.

At this stage no additional configuration is needed - by default the CSRF form field and cookie name will be set to csrf-token, and the filter will verify the token. But you can change these names if you would like:

quarkus.csrf-reactive.form-field-name=csrftoken
quarkus.csrf-reactive.cookie-name=csrftoken

Note that the CSRF filter has to read and cache the input stream in order to verify the token. However if you prefer you can compare the CSRF form field and cookie values in the application code:

package io.quarkus.it.csrf;

import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;

@Path("/service")
public class UserNameResource {

    @Inject
    Template csrfToken;

    @GET
    @Path("/csrfTokenForm")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance getCsrfTokenForm() {
        return csrfToken.instance();
    }

    @POST
    @Path("/csrfTokenForm")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.TEXT_PLAIN)
    public String postCsrfTokenForm(@CookieParam("csrf-token") Cookie csrfCookie, @FormParam("csrf-token") String formCsrfToken, @FormParam("name") String userName) {
        if (!csrfCookie.getValue().equals(formCsrfToken)) { (1)
            throw new BadRequestException();
        }
        return userName;
    }
}
1 Compare the CSRF form field and cookie values and fail with HTTP status 400 if they don’t match.

Also disable the token verification in the filter:

quarkus.csrf-reactive.verify-token=false

配置参考

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认

If filter is enabled.

Environment variable: QUARKUS_CSRF_REACTIVE_ENABLED

Show more

boolean

true

Form field name which keeps a CSRF token.

Environment variable: QUARKUS_CSRF_REACTIVE_FORM_FIELD_NAME

Show more

string

csrf-token

CSRF cookie name.

Environment variable: QUARKUS_CSRF_REACTIVE_COOKIE_NAME

Show more

string

csrf-token

CSRF cookie max age.

Environment variable: QUARKUS_CSRF_REACTIVE_COOKIE_MAX_AGE

Show more

Duration

10M

CSRF cookie path.

Environment variable: QUARKUS_CSRF_REACTIVE_COOKIE_PATH

Show more

string

/

CSRF cookie domain.

Environment variable: QUARKUS_CSRF_REACTIVE_COOKIE_DOMAIN

Show more

string

If enabled the CSRF cookie will have its 'secure' parameter set to 'true' when HTTP is used. It may be necessary when running behind an SSL terminating reverse proxy. The cookie will always be secure if HTTPS is used even if this property is set to false.

Environment variable: QUARKUS_CSRF_REACTIVE_COOKIE_FORCE_SECURE

Show more

boolean

false

Create CSRF token only if the HTTP GET relative request path matches one of the paths configured with this property. Use a comma to separate multiple path values.

Environment variable: QUARKUS_CSRF_REACTIVE_CREATE_TOKEN_PATH

Show more

list of string

The random CSRF token size in bytes.

Environment variable: QUARKUS_CSRF_REACTIVE_TOKEN_SIZE

Show more

int

16

Verify CSRF token in the CSRF filter. If this property is enabled then the input stream will be read and cached by the CSRF filter to verify the token. If you prefer then you can disable this property and compare CSRF form and cookie parameters in the application code using JAX-RS javax.ws.rs.FormParam which refers to the form-field-name form property and javax.ws.rs.CookieParam which refers to the CsrfReactiveConfig#cookieName cookie. Note that even if the CSRF token verification in the CSRF filter is disabled, the filter will still perform checks to ensure the token is available, has the correct token-size in bytes and that the Content-Type HTTP header is either 'application/x-www-form-urlencoded' or 'multipart/form-data'.

Environment variable: QUARKUS_CSRF_REACTIVE_VERIFY_TOKEN

Show more

boolean

true

Require that only 'application/x-www-form-urlencoded' or 'multipart/form-data' body is accepted for the token verification to proceed. Disable this property for the CSRF filter to avoid verifying the token for POST requests with other content types. This property is only effective if verify-token property is enabled.

Environment variable: QUARKUS_CSRF_REACTIVE_REQUIRE_FORM_URL_ENCODED

Show more

boolean

true

About the Duration format

持续时间的格式使用标准的 java.time.Duration 格式您可以在 Duration#parse() javadoc 中了解更多信息。

您还可以提供以数字开头的持续时间值。 在这种情况下,如果该值仅包含一个数字,则转换器将该值视为秒。 否则,PT 会隐式添加到值的前面,以获得标准的 java.time.Duration 格式。