Cross-Site Request Forgery Prevention
クロスサイトリクエストフォージェリ(CSRF) とは、エンドユーザーが現在認証されているウェブアプリケーション上で意図しないアクションを実行させる攻撃です。
Quarkus Securityは、CSRF防止機能を提供しています。この機能は、'application/x-www-form-urlencoded' および 'multipart/form-data' フォームのCSRFトークンを作成および検証する RESTEasy Reactive サーバーフィルター、Qute テンプレートへのCSRFトークン注入 をサポートするQute HTML form parameterプロバイダーからなります。
プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
このコマンドは、 csrf-reactive エクステンションをインポートするプロジェクトを生成します。
Quarkusプロジェクトをすでに設定している場合は、プロジェクトのベースディレクトリで次のコマンドを実行することで、 csrf-reactive エクステンションをプロジェクトに追加できます:
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")
次に、このテンプレートを含むファイルを csrfToken.html と名付け、 src/main/resources/templates フォルダに置いてください:
<!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 | この書式は、CSRF トークンを隠しフォームフィールドに注入するために使用されます。このトークンは CSRF フィルタによって CSRF クッキーと照合され検証されます。 |
では、HTMLフォームを返し、フォームのPOSTリクエストを処理するリソースクラスを作成しましょう:
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 | csrfToken.html を Template として注入してください。 |
| 2 | CSRFフィルターによって作成されたCSRFトークンを含むhiddenフォームフィールドを持つHTMLフォームを返します。 |
| 3 | フォームの POST リクエストを処理します。このメソッドは、CSRF フィルタがトークンの確認に成功した場合にのみ呼び出すことができます。 |
フィルタが、隠された CSRF フォームフィールドがない、CSRF クッキーがない、あるいは CSRF フォームフィールドと CSRF クッキーの値が一致しないことを発見した場合、フォーム POST リクエストは HTTP ステータス 400 で失敗します。
この段階では、追加の設定は必要ありません。デフォルトでは、CSRFフォームフィールドとクッキー名は csrf-token に設定され、フィルタはトークンを検証します。しかし、必要であれば、これらの名前を変更することができます:
quarkus.csrf-reactive.form-field-name=csrftoken
quarkus.csrf-reactive.cookie-name=csrftoken
CSRFフィルタはトークンを検証するために入力ストリームを読み込んでキャッシュする必要があることに注意してください。しかし、希望におうじて、アプリケーション・コードでCSRFフォーム・フィールドとクッキーの値を比較することもできます:
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 | CSRFのフォームフィールドとクッキーの値を比較し、一致しない場合はHTTPステータス 400 で失敗します。 |
また、フィルターのトークン検証を無効にします:
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 |
文字列のリスト |
|
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 |
|
|
期間フォーマットについて
期間のフォーマットは標準の 数値で始まる期間の値を指定することもできます。この場合、値が数値のみで構成されている場合、コンバーターは値を秒として扱います。そうでない場合は、 |