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.
创建项目
首先,我们需要一个新的项目。使用以下命令创建一个新的项目:
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:
quarkus extension add 'csrf-reactive'
./mvnw quarkus:add-extension -Dextensions='csrf-reactive'
./gradlew addExtension --extensions='csrf-reactive'
这将在您的构建文件中添加以下内容:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-csrf-reactive</artifactId>
</dependency>
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
类型 |
默认 |
|
|---|---|---|
If filter is enabled. Environment variable: Show more |
boolean |
|
Form field name which keeps a CSRF token. Environment variable: Show more |
string |
|
CSRF cookie name. Environment variable: Show more |
string |
|
CSRF cookie max age. Environment variable: Show more |
|
|
CSRF cookie path. Environment variable: Show more |
string |
|
CSRF cookie domain. Environment variable: 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: Show more |
boolean |
|
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: Show more |
list of string |
|
The random CSRF token size in bytes. Environment variable: Show more |
int |
|
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 Environment variable: Show more |
boolean |
|
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 Environment variable: Show more |
boolean |
|
|
About the Duration format
持续时间的格式使用标准的 您还可以提供以数字开头的持续时间值。 在这种情况下,如果该值仅包含一个数字,则转换器将该值视为秒。 否则, |