Csrf Token im DC_Table-Kontext
Hallo,
ich habe in den letzten Tagen an meiner Erweiterung für Isotope iso_productfeed weiter gearbeitet (Der main-branch ist der aktuelle Stand).
Ich habe darin die TC_Table erweitert um ein kleines Feedback-Kontaktformular zu integrieren. Ich habe alles soweit gut hinbekommen. Die Tokens sind identisch, aber ich bekomme trotzdem die Fehlermeldung:
Code:
Invalid CSRF token. Please reload the page and try again.
beim absenden.
Hat Jemand vielleicht das gleiche schonmal programmiert und kann mir da helfen. Ich weiß echt nicht, was da falsch läuft.
Warscheinlich muss man sich die Erweiterung installieren um den Code zu debuggn.
Ansonsten wären hier die zwei wichtigsten Klassen:
1.
Code:
<?php
namespace Bits\IsoProductfeed\Contao;
use Contao\DC_Table;
use Contao\System;
use Contao\Environment as ContaoEnvironment;
use Twig\Environment;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Bits\IsoProductfeed\Form\FeedbackType;
class DC_IsoProductfeed extends DC_Table
{
protected $strTable = 'tl_iso_productfeed';
public function __construct($strTable)
{
parent::__construct($strTable);
}
public function showAll()
{
// Hole die aktuelle Request aus dem Symfony-Container
$request = System::getContainer()->get('request_stack')->getCurrentRequest();
return $this->generateForm($request) . parent::showAll();
}
private function processForm(array $data)
{
// Daten speichern oder verarbeiten
return 'Formular erfolgreich verarbeitet.';
}
public function generateForm($request)
{
// Formular erstellen
$container = System::getContainer();
$formFactory = $container->get('form.factory');
// Hole den CSRF-Token-Manager aus dem Container
$csrfTokenManager = System::getContainer()->get('security.csrf.token_manager');
$csrfToken = $csrfTokenManager->getToken('feedback_form')->getValue();
$form = $formFactory->create(FeedbackType::class, null, [
'action' => ContaoEnvironment::get('base').ContaoEnvironment::get('request'),
'csrf_token' => $csrfToken,
]);
//var_dump('1: '. $csrfToken);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
return $this->processForm($data);
}
$twig = $container->get('twig');
return $twig->render('@Contao/iso_productfeed_panel.html.twig', [
'form' => $form->createView(),
]);
}
}
2.
Code:
<?php
namespace Bits\IsoProductfeed\Form;
use Contao\System;
use Bits\IsoProductfeed\Entity\Feedback;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
class FeedbackType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$container = System::getContainer();
$formFactory = $container->get('form.factory');
//var_dump('2: '.$options['csrf_token']);
$builder
->add('name')
->add('email')
->add('message')
->add('_token', HiddenType::class, [
'data' => $options['csrf_token'], // Möglichkeit, CSRF-Token als Option zu übergeben
])
->add('submit', SubmitType::class, [
'label' => 'Senden',
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Feedback::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'feedback_form',
'csrf_token' => Null
]);
}
}
Achso und die services.yaml:
Code:
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters: null
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
Bits\IsoProductfeed\Contao\DC_IsoProductfeed:
arguments:
$twig: '@twig'
Symfony\Component\Security\Csrf\CsrfTokenManagerInterface: '@security.csrf.token_manager'
Bits\IsoProductfeed\EventListener\DataContainer\IsoProductfeedOnSaveListener:
tags: ['contao.callback']
Bits\IsoProductfeed\EventListener\DataContainer\IsoProductsOnSaveListener:
tags: ['contao.callback']
Symfony\Component\Form\FormFactoryInterface:
alias: 'form.factory'
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
Bits\IsoProductfeed\:
resource: ../../../src
exclude:
- ../../../src/{ContaoManager,DependencyInjection,Resources}
# - '../../../src/Backend/ConfigModule.php'
# - '../../../src/Eventlistener/'
Danke schonmal :-)
Das ist das Post Array, der Token ist mit den den ich absende identisch
Code:
array(6) { ["name"]=> string(17) "Monique Hahnefeld" ["email"]=> string(25) "info@monique-hahnefeld.de" ["message"]=> string(4) "Test" ["FORM_SUBMIT"]=> string(13) "feedback_form" ["REQUEST_TOKEN"]=> string(124) "7edfc66b2246cb164d257.1PghUG21C0M1W4-KnpWfuc_EEI2zRBEhukvnnr_C6M4.kMtwJD-CRAFQBL_DzvfT8oDyYdnmHWFq9Sepp_qIoJq4gWljK9Yybl0a6A" ["submit"]=> string(0) "" }
Ich habe jetzt herausgefunden wo es als nicht Valid gilt:
PHP-Code:
/**
* Skip the CSRF token validation if the request has no cookies, no
* authenticated user and the session has not been started.
*/
public function canSkipTokenValidation(Request $request, string $tokenCookieName): bool
{
return
!$request->getUserInfo()
&& (
0 === $request->cookies->count()
|| [$tokenCookieName] === $request->cookies->keys()
)
&& $this->isSessionEmpty($request);
}
Diese Funktion ist in der Contao\CoreBundle\CsrfContaoCsrfTokenManager-Klasse.
Also hat es irgendwas mit Cookies und der Session zu tun.
Also ich konnte jetzt noch herausfinden, dass das hier nicht hinhaut:
PHP-Code:
[$tokenCookieName] === $request->cookies->keys()
Da bin ich echt ratlos:rolleyes:
Liste der Anhänge anzeigen (Anzahl: 1)
Hi. Also ich extende den DC_Table mit dieser Klasse. Darin wird das Formular generiert. Das Formular ist so konfiguriert das es auf der selben Seite bleibt, wenn es abgesendet wird.
PHP-Code:
<?php
namespace Bits\IsoProductfeed\Contao;
use Contao\DC_Table;
use Contao\System;
use Contao\Environment as ContaoEnvironment;
use Twig\Environment;
use Contao\CoreBundle\Security\ContaoCsrfTokenManager;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Bits\IsoProductfeed\Form\FeedbackType;
class DC_IsoProductfeed extends DC_Table
{
protected $strTable = 'tl_iso_productfeed';
public function __construct( $strTable
){
parent::__construct($strTable);
}
public function showAll()
{
// Hole die aktuelle Request aus dem Symfony-Container
$request = System::getContainer()->get('request_stack')->getCurrentRequest();
return $this->generateForm($request) . parent::showAll();
}
private function processForm(array $data)
{
// Daten speichern oder verarbeiten
return 'Formular erfolgreich verarbeitet.';
}
public function generateForm($request)
{
// Formular erstellen
$container = System::getContainer();
$formFactory = $container->get('form.factory');
// Hole den CSRF-Token-Manager aus dem Container
$csrfTokenManager = System::getContainer()->get('contao.csrf.token_manager');
$form = $formFactory->create(FeedbackType::class, null, [
'action' => $request->getUri(),
'csrf_protection' => true,
'csrf_field_name' => 'REQUEST_TOKEN',
'csrf_token_id' => 'feedback_form',
'attr' => [
'id' => 'feedback_form', // Setzt die ID des Formulars
'name' => 'feedback_form', // Setzt die ID des Formulars
],
'csrf_token_manager' => $csrfTokenManager
]);
var_dump(session_id());
var_dump($request->request->all());
var_dump('1: '. $csrfTokenManager->getToken('feedback_form')->getValue());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
var_dump('Submitted Token: ' . $request->request->get('REQUEST_TOKEN'));
var_dump($data);exit;
return $this->processForm($data);
}
$twig = $container->get('twig');
return $twig->render('@Contao/iso_productfeed_panel.html.twig', [
'form' => $form->createView(),
]);
}
}
Anhang 27597