Implement middleware for CSRF token verification

This fixes a rather large oversight in Flarum's codebase, which was that
we had no explicit CSRF protection using the traditional token approach.

The JS frontend was actually sending these tokens, but the backend did
not require them.
This commit is contained in:
Franz Liedke 2019-06-09 00:25:26 +02:00
parent 69fdd82ffc
commit aa43d1475e
5 changed files with 51 additions and 0 deletions

View File

@ -61,6 +61,7 @@ class AdminServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(Middleware\RequireAdministrateAbility::class));

View File

@ -57,6 +57,7 @@ class ApiServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithHeader::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
event(new ConfigureMiddleware($pipe, 'api'));

View File

@ -68,6 +68,7 @@ class ForumServiceProvider extends AbstractServiceProvider
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
$pipe->pipe($app->make(HttpMiddleware\ShareErrorsFromSession::class));

View File

@ -44,6 +44,8 @@ class AuthenticateWithHeader implements Middleware
$token->touch();
$actor = $token->user;
$request = $request->withAttribute('bypassCsrfToken', true);
}
if (isset($actor)) {

View File

@ -0,0 +1,46 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Http\Middleware;
use Flarum\Http\Exception\TokenMismatchException;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as Handler;
class CheckCsrfToken implements Middleware
{
public function process(Request $request, Handler $handler): Response
{
if (in_array($request->getMethod(), ['GET', 'HEAD', 'OPTIONS'])) {
return $handler->handle($request);
}
if ($request->getAttribute('bypassCsrfToken', false)) {
return $handler->handle($request);
}
if ($this->tokensMatch($request)) {
return $handler->handle($request);
}
throw new TokenMismatchException('CSRF token did not match');
}
private function tokensMatch(Request $request): bool
{
$expected = (string) $request->getAttribute('session')->token();
$provided = $request->getHeaderLine('X-CSRF-Token'); // TODO: Use form field, if provided
return hash_equals($expected, $provided);
}
}