Reject requests for `RquestHandlerInterface` implementations without a valid XSRF...
authorAlexander Ebert <ebert@woltlab.com>
Mon, 14 Nov 2022 18:28:47 +0000 (19:28 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 14 Nov 2022 18:28:47 +0000 (19:28 +0100)
GET and HEAD requests are always exempt from the validation, because these are by definition safe actions (*).

(*) Legacy implementations violated this principle, but this is a bad practice and is frowned upon in new PSR implementation.

wcfsetup/install/files/lib/http/middleware/Xsrf.class.php

index acb986ecb2d5b95e866671100416c9380ba1472f..028cc094b725fd54a719c31fba2abb457c3e1f11 100644 (file)
@@ -6,6 +6,9 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use wcf\system\exception\InvalidSecurityTokenException;
+use wcf\system\request\Request;
+use wcf\system\request\RequestHandler;
 use wcf\system\WCF;
 
 /**
@@ -23,6 +26,13 @@ final class Xsrf implements MiddlewareInterface
 
     public const HAS_VALID_HEADER_ATTRIBUTE = self::class . "\0hasValidHeader";
 
+    private readonly RequestHandler $requestHandler;
+
+    public function __construct()
+    {
+        $this->requestHandler = RequestHandler::getInstance();
+    }
+
     /**
      * @inheritDoc
      */
@@ -34,11 +44,30 @@ final class Xsrf implements MiddlewareInterface
             self::TOKEN_ATTRIBUTE,
             $xsrfToken
         );
+
+        $hasValidXsrfToken = \hash_equals($xsrfToken, $request->getHeaderLine('x-xsrf-token'));
+
         $request = $request->withAttribute(
             self::HAS_VALID_HEADER_ATTRIBUTE,
-            \hash_equals($xsrfToken, $request->getHeaderLine('x-xsrf-token'))
+            $hasValidXsrfToken
         );
 
+        if (
+            $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();
+            }
+        }
+
         return $handler->handle($request);
     }
+
+    private function validateXsrfToken(Request $request, $hasValidXsrfToken): bool
+    {
+        return $hasValidXsrfToken;
+    }
 }