addEventSubscriber($this); } public static function getSubscribedEvents(): array { return [FormEvents::PRE_SUBMIT => 'preSubmit']; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(['nonce_protection' => true]); } /** * Before the form gets to the controller, piggy-back on the CSRF token and use it as a nonce, ensuring it was only submitted once */ public function preSubmit(FormEvent $event) { $form = $event->getForm(); $postRequestSizeExceeded = 'POST' === $form->getConfig()->getMethod() && $this->serverParams->hasPostMaxSizeBeenExceeded(); if ($form->isRoot() && $form->getConfig()->getOption('compound') && !$postRequestSizeExceeded) { $data = $event->getData(); $token_id = $form->getConfig()->getOption('csrf_token_id') ?: ($form->getName() ?: \get_class($form->getConfig()->getType()->getInnerType())); $token_value = \is_string($data['_token'] ?? null) ? $data['_token'] : null; $csrf_token = new CsrfToken($token_id, $token_value); if (null === $token_value || !$this->token_manager->isTokenValid($csrf_token) || Cache::incr("nonce:{$token_value}") !== 1) { // TODO add TTL $form->addError(new FormError(_m('Invalid nonce'), null, [], null, $csrf_token)); } } } }