Подтвердить что ты не робот

Параметры аутентификации клиента + form_login ломают все токены csrf

У меня есть система Symfony 3.3.13 с различными формами.

Чтобы достичь "глубокой связи" в этих формах, т.е. будучи в состоянии щелкнуть по ссылке электронной почты, войти и затем перенаправляться в форму, я добавил следующие изменения:

config.yml

framework:
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~
    ...
    more
    ...

security.yml

security:
    providers:
        zog:
            id: app.zog_user_provider


    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            logout:
                path:   /logout
                target: /
            guard:
                authenticators:
                    - app.legacy_token_authenticator
                    - app.token_authenticator
                entry_point: app.legacy_token_authenticator
            form_login:                                         <--this line alone breaks CSRF 
                use_referer: true                               <--I tried partial combinations, none seems to make CSRF work
                login_path: /security/login
                use_forward: true
                success_handler: login_handler
                csrf_token_generator: security.csrf.token_manager   <--added based on answer, doesn't help

SRC/AppBundle/Ресурсы/конфигурации/services.yml

login_handler:
    class: AppBundle\Service\LoginHandler
    arguments: ['@router', '@doctrine.orm.entity_manager', '@service_container']

ЦСИ/AppBundle/Услуги/Loginhandler.php

<?php
/**
 * Created by PhpStorm.
 * User: jochen
 * Date: 11/12/17
 * Time: 12:31 PM
 */

namespace AppBundle\Service;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Routing\RouterInterface;
use Doctrine\ORM\EntityManager;

class LoginHandler implements AuthenticationSuccessHandlerInterface
{
    private $router;
    private $container;
    private static $key;

    public function __construct(RouterInterface $router, EntityManager $em, $container) {

        self::$key = '_security.main.target_path';

        $this->router = $router;
        $this->em = $em;
        $this->session = $container->get('session');

    }

    public function onAuthenticationSuccess( Request $request, TokenInterface $token ) {

        //check if the referer session key has been set
        if ($this->session->has( self::$key )) {

            //set the url based on the link they were trying to access before being authenticated
            $route = $this->session->get( self::$key );

            //remove the session key
            $this->session->remove( self::$key );
            //if the referer key was never set, redirect to a default route
            return new RedirectResponse($route);
        } else{

            $url = $this->generateUrl('portal_job_index');

            return new RedirectResponse($url);

        }



    }
}

Я также убедился, что csrf включен в форме входа в систему следующим образом:

SRC/AppBundle/ресурсы/виды/безопасность/login.html.twig

        <form action="{{ path('app_security_login') }}" method="post" autocomplete="off">
            <input type="hidden" name="_csrf_token"
                   value="{{ csrf_token('authenticate') }}"
            >

приложение/Config/services.yml

app.legacy_token_authenticator:
    class: AppBundle\Security\LegacyTokenAuthenticator
    arguments: ["@router", "@session", "%kernel.environment%", "@security.csrf.token_manager"]

ЦСИ/AppBundle/Безопасность\legacyTokenAuthenticator

    class LegacyTokenAuthenticator extends AbstractGuardAuthenticator
    {
        private $session;

        private $router;

        private $csrfTokenManager;

        public function __construct(
            RouterInterface $router,
            SessionInterface $session,
            $environment,
            CsrfTokenManagerInterface $csrfTokenManager
        ) {
            if ($environment != 'test'){
                session_start();
            }
            $session->start();
            $this->setSession($session);
            $this->csrfTokenManager = $csrfTokenManager;
            $this->router = $router;
        }


        /**
         * @return mixed
         */
        public function getSession()
        {
            return $this->session;
        }


        /**
         * @param mixed $session
         */
        public function setSession($session)
        {
            $this->session = $session;
        }


        /**
         * Called on every request. Return whatever credentials you want,
         * or null to stop authentication.
         */
        public function getCredentials(Request $request)
        {
            $csrfToken = $request->request->get('_csrf_token');

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

            if (isset($_SESSION['ADMIN_logged_in']) && intval($_SESSION['ADMIN_logged_in'])){
                return $_SESSION['ADMIN_logged_in'];
            }
            return;
        }

        public function getUser($credentials, UserProviderInterface $userProvider)
        {
            return $userProvider->loadUserByUserId($credentials);
        }

        public function checkCredentials($credentials, UserInterface $user)
        {
            return $user->getUsername() == $credentials;
        }

        public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
        {
            return null;
        }

        public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
        {
            return null;
        }

        /**
         * Called when authentication is needed, but it not sent
         */
        public function start(Request $request, AuthenticationException $authException = null)
        {
            $url = $this->router->generate('app_security_login');
            return new RedirectResponse($url);
        }

        public function supportsRememberMe()
        {
            return false;
        }


    }

Все проверки CSRF - в том числе и в форме входа - всегда срабатывают, когда я добавляю 5 строк в security.yml, начиная с form_login. Ошибка, которую я получаю:

The CSRF token is invalid. Please try to resubmit the form. portalbundle_portal_job 

Вызвано:

Когда я удаляю эти 5 строк, все токены CSRF работают.

4b9b3361

Ответ 1

Вот файл security.yml, который у меня есть из одного из моих проектов, в котором включена защита csrf. Я использую FOS UserBundle, который выглядит отличным от вашего, но вы можете увидеть что-то здесь, что помогает. В частности, должен быть указан генератор csrf для использования FOS UserBundle (под брандмауэрами: main: form_login). У меня также есть настройки шаблонов access_control, так что некоторые конечные точки доступны, только если пользователь аутентифицирован с определенной ролью - но я не думаю, что это повлияет на csrf. См. Ниже:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    firewalls:
        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            logout:       true
            anonymous:    true

    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/event, role: ROLE_USER }

Также в моем основном config.yml я включил csrf в рамках. Здесь отрывок всего:

framework:
    #esi:             ~
    translator:      { fallbacks: ["%locale%"] }
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~

Ответ 2

Для меня ручное обращение с токенами Symfony CSRF - это главный головной кошелек. Не говоря уже о том, как это сделать, я думаю, что почти всегда есть более простое решение.

Мое решение для защиты CSRF при входе в систему не затрагивает эту проблему.

Я создаю регистрацию формы с помощью компонента Form.

function loginAction()
{
    $login = $this->createForm(LoginType::class);
    $authenticationUtils = $this->get('security.authentication_utils');
    $error = $authenticationUtils->getLastAuthenticationError();

    return $this->render('Path/to/login.html.twig', [
        'form' => $login->createView(),
        'error' => $error,
    ]);
}

LoginType.php:

use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
//...

class LoginType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('username', TextType::class)
            ->add('password', PasswordType::class)
        ;
    }
}

login.html.twig:

{# template information #}
{{ form_start(form) }}
    {{ form_row(form.username, {
        'full_name': '_username'
    } ) }}
    {{ form_row(form.password, {
        'full_name': '_password'
    } ) }}
{{ form_end(form) }}
{# template information #}

security.yml:

security:
    providers:
        zog:
            id: app.zog_user_provider


    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            logout:
                path:   /logout
                target: /
            form_login:
                use_referer: true                  
                login_path: /security/login
                success_handler: login_handler
                always_use_default_target_path: false
                default_target_path: /

Если у вас включена поддержка CSRF в формах, ваша форма входа будет защищена CSRF без какого-либо пользовательского аутентификатора Guard.