Skip to content

Commit

Permalink
Added automatically CSRF protected authenticators
Browse files Browse the repository at this point in the history
  • Loading branch information
wouterj committed Apr 20, 2020
1 parent bf1a452 commit 60d396f
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 10 deletions.
Expand Up @@ -64,6 +64,11 @@
<argument type="abstract">stateless firewall keys</argument>
</service>

<service id="security.listener.csrf_protection" class="Symfony\Component\Security\Http\EventListener\CsrfProtectionListener">
<tag name="kernel.event_subscriber" />
<argument type="service" id="security.csrf.token_manager" />
</service>

<service id="security.listener.remember_me"
class="Symfony\Component\Security\Http\EventListener\RememberMeListener"
abstract="true">
Expand Down
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Http\Authenticator;

/**
* This interface can be implemented to automatically add CSF
* protection to the authenticator.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
interface CsrfProtectedAuthenticatorInterface
{
/**
* An arbitrary string used to generate the value of the CSRF token.
* Using a different string for each authenticator improves its security.
*/
public function getCsrfTokenId(): string;

/**
* Returns the CSRF token contained in credentials if any.
*
* @param mixed $credentials the credentials returned by getCredentials()
*/
public function getCsrfToken($credentials): ?string;
}
Expand Up @@ -32,7 +32,7 @@
* @final
* @experimental in 5.1
*/
class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements PasswordAuthenticatedInterface
class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements PasswordAuthenticatedInterface, CsrfProtectedAuthenticatorInterface
{
use TargetPathTrait;

Expand Down Expand Up @@ -113,17 +113,15 @@ public function getUser($credentials): ?UserInterface
return $this->userProvider->loadUserByUsername($credentials['username']);
}

/* @todo How to do CSRF protection?
public function checkCredentials($credentials, UserInterface $user): bool
public function getCsrfTokenId(): string
{
if (null !== $this->csrfTokenManager) {
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $credentials['csrf_token']))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
return $this->options['csrf_token_id'];
}

return $this->checkPassword($credentials, $user);
}*/
public function getCsrfToken($credentials): ?string
{
return $credentials['csrf_token'];
}

public function createAuthenticatedToken(UserInterface $user, $providerKey): TokenInterface
{
Expand Down
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Http\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Http\Authenticator\CsrfProtectedAuthenticatorInterface;
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;

class CsrfProtectionListener implements EventSubscriberInterface
{
private $csrfTokenManager;

public function __construct(CsrfTokenManagerInterface $csrfTokenManager)
{
$this->csrfTokenManager = $csrfTokenManager;
}

public function verifyCredentials(VerifyAuthenticatorCredentialsEvent $event): void
{
$authenticator = $event->getAuthenticator();
if (!$authenticator instanceof CsrfProtectedAuthenticatorInterface) {
return;
}

$csrfTokenValue = $authenticator->getCsrfToken($event->getCredentials());
if (null === $csrfTokenValue) {
return;
}

$csrfToken = new CsrfToken($authenticator->getCsrfTokenId(), $csrfTokenValue);
if (false === $this->csrfTokenManager->isTokenValid($csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}

public static function getSubscribedEvents(): array
{
return [VerifyAuthenticatorCredentialsEvent::class => ['verifyCredentials', 256]];
}
}

0 comments on commit 60d396f

Please sign in to comment.