Add `DisableXsrfCheck` attribute to opt out of the automated XSRF validation
authorAlexander Ebert <ebert@woltlab.com>
Mon, 14 Nov 2022 18:50:19 +0000 (19:50 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 14 Nov 2022 18:50:19 +0000 (19:50 +0100)
wcfsetup/install/files/lib/http/middleware/Xsrf.class.php

index 028cc094b725fd54a719c31fba2abb457c3e1f11..94b8fabd091f36c9b85b162e484e18752b20ac25 100644 (file)
@@ -56,18 +56,33 @@ final class Xsrf implements MiddlewareInterface
             $request->getMethod() !== 'GET'
             && $request->getMethod() !== 'HEAD'
             && $this->requestHandler->getActiveRequest()
-            && \is_subclass_of($this->requestHandler->getActiveRequest()->getClassName(), RequestHandlerInterface::class)
         ) {
-            if (!$this->validateXsrfToken($this->requestHandler->getActiveRequest(), $hasValidXsrfToken)) {
-                throw new InvalidSecurityTokenException();
-            }
+            $this->assertHasValidXsrfToken($this->requestHandler->getActiveRequest(), $hasValidXsrfToken);
         }
 
         return $handler->handle($request);
     }
 
-    private function validateXsrfToken(Request $request, $hasValidXsrfToken): bool
+    private function assertHasValidXsrfToken(Request $request, $hasValidXsrfToken): void
     {
-        return $hasValidXsrfToken;
+        if (!\is_subclass_of($request->getClassName(), RequestHandlerInterface::class)) {
+            // Skip the XSRF check for legacy controllers.
+            return;
+        }
+
+        $reflectionClass = new \ReflectionClass($request->getClassName());
+        if ($reflectionClass->getAttributes('DisableXsrfCheck') !== []) {
+            // Controller has opted out of the XSRF check.
+            return;
+        }
+
+        if (!$hasValidXsrfToken) {
+            throw new InvalidSecurityTokenException();
+        }
     }
 }
+
+#[\Attribute(\Attribute::TARGET_CLASS)]
+final class DisableXsrfCheck
+{
+}