客户身份验证器 + form_login 选项会破坏所有 csrf 令牌
Posted
技术标签:
【中文标题】客户身份验证器 + form_login 选项会破坏所有 csrf 令牌【英文标题】:customer authenticator + form_login options break all csrf tokens 【发布时间】:2018-05-25 13:13:45 【问题描述】:我有一个各种形式的 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/Resources/config/services.yml
login_handler:
class: AppBundle\Service\LoginHandler
arguments: ['@router', '@doctrine.orm.entity_manager', '@service_container']
src/AppBundle/Service/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/resources/views/security/login.html.twig
<form action=" path('app_security_login') " method="post" autocomplete="off">
<input type="hidden" name="_csrf_token"
value=" csrf_token('authenticate') "
>
app/config/services.yml
app.legacy_token_authenticator:
class: AppBundle\Security\LegacyTokenAuthenticator
arguments: ["@router", "@session", "%kernel.environment%", "@security.csrf.token_manager"]
src/AppBundle/Security\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's 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;
当我在 security.yml 中添加以 form_login 开头的 5 行时,所有 CSRF 检查(包括登录表单)总是失败。我得到的错误是:
The CSRF token is invalid. Please try to resubmit the form. portalbundle_portal_job
原因:
当我删除这 5 行时,所有 CSRF 令牌都可以工作。
【问题讨论】:
你关注了这个文档吗:symfony.com/doc/current/security/csrf_in_login_form.html ? 不具体,所有的表单在登录的时候都会受到影响。这个有关系吗? 只是为了确认,登录表单没有 CSRF。但我的问题是指系统中的所有表格 哪个 Symfony 版本? Symfony 版本是 3.3.13 【参考方案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: ~
【讨论】:
谢谢,不幸的是这两行并没有什么不同。我还在登录表单中添加了 csrf,只是为了实现一致性,这也失败了【参考方案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 身份验证器。
【讨论】:
以上是关于客户身份验证器 + form_login 选项会破坏所有 csrf 令牌的主要内容,如果未能解决你的问题,请参考以下文章
我在服务器上的iis上的身份验证选项中去掉了匿名访问,启用了集成windows身份验证
AAD B2C 中具有客户端凭据授予流的 Azure 应用服务轻松身份验证