Skip to content

docs(form): CSRF protection description is incorrect — SetAntiCsrfTrait alone enforces verification #360

Description

@koriym

The "CSRF Protections" / "CSRF" sections in manuals/1.0/en/form.md:136 and manuals/1.0/ja/form.md:136 state:

A form that uses SetAntiCsrfTrait is wired with an AntiCsrfInterface, but the token is only verified on methods annotated with #[CsrfProtection]. Methods without #[CsrfProtection] perform no CSRF check even if the form has an AntiCsrf object set.

This does not match the implementation in ray/web-form-module 1.0.

What actually happens

SetAntiCsrfTrait::setAntiCsrf() is annotated with #[Inject], so DI populates $this->antiCsrf at construction. AbstractForm::postConstruct() then calls enableAntiCsrf() which adds the token field, and AbstractForm::apply() throws CsrfViolationException on token mismatch — regardless of whether the validated method has #[CsrfProtection].

This is exercised by AbstractFormTest::testAntiCsrfViolation (source), which sets antiCsrf directly on the form (no interceptor, no attribute) and asserts the throw.

Two independent opt-in paths

  • Per-form: use SetAntiCsrfTrait; on the form — always-on for that form.
  • Per-action: #[CsrfProtection] on the controller/resource method — AuraInputInterceptor injects antiCsrf before apply().

Either path enables enforcement.

Related fix upstream

The same wording in ray-di/Ray.WebFormModule README was corrected in ray-di/Ray.WebFormModule#30. The docs site needs the same correction in both EN and JA pages.

Proposed patch

git apply 可能なパッチ
--- a/manuals/1.0/en/form.md
+++ b/manuals/1.0/en/form.md
@@ -133,7 +133,12 @@
 
 ## CSRF Protections
 
-CSRF protection is **opt-in**. A form that uses `SetAntiCsrfTrait` is wired with an `AntiCsrfInterface`, but the token is only verified on methods annotated with `#[CsrfProtection]`. Methods without `#[CsrfProtection]` perform no CSRF check even if the form has an `AntiCsrf` object set.
+CSRF protection is **opt-in** and can be enabled through either of two independent paths:
+
+- **Per-form**: add `use SetAntiCsrfTrait;` to the form. `AntiCsrfInterface` is injected at construction time, the token field is added in `postConstruct()`, and every `apply()` call verifies the token.
+- **Per-action**: annotate the validated method with `#[CsrfProtection]`. `AuraInputInterceptor` then injects `AntiCsrfInterface` into the form before `apply()` runs.
+
+Either path causes `AbstractForm::apply()` to throw `CsrfViolationException` on token mismatch. Without either path, no CSRF check is performed.
 
 ```php
 use BEAR\Resource\ResourceObject;
--- a/manuals/1.0/ja/form.md
+++ b/manuals/1.0/ja/form.md
@@ -133,7 +133,12 @@
 
 ## CSRF
 
-CSRF(クロスサイトリクエストフォージェリ)保護はopt-inです。フォームに`SetAntiCsrfTrait`を使うと`AntiCsrfInterface`が組み込まれますが、トークンの検証は`#[CsrfProtection]`アトリビュートを付けたメソッドでのみ実行されます。アトリビュートが無いメソッドでは、フォームが`AntiCsrf`オブジェクトを持っていてもCSRF検証は行われません。
+CSRF(クロスサイトリクエストフォージェリ)保護は**opt-in**で、独立した2つの経路のいずれかで有効化できます。
+
+- **フォーム単位**: フォームに`use SetAntiCsrfTrait;`を追加します。DIで`AntiCsrfInterface`が注入され、`postConstruct()`でトークンフィールドが追加され、`apply()`の呼び出しごとにトークンが検証されます。
+- **アクション単位**: バリデーション対象のメソッドに`#[CsrfProtection]`を付与します。`AuraInputInterceptor`が`apply()`実行前に`AntiCsrfInterface`をフォームへ注入します。
+
+どちらの経路でも、トークン不一致時には`AbstractForm::apply()`が`CsrfViolationException`をthrowします。どちらも使わない場合はCSRF検証は行われません。
 
 ```php
 use BEAR\Resource\ResourceObject;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions