Commit b28eaa79 authored by alexpott's avatar alexpott

Issue #2941102 by gease, martin107, kfritsche, hchonov, alexpott: Form has...

Issue #2941102 by gease, martin107, kfritsche, hchonov, alexpott: Form has become outdated after login (CSRF token race condition)
parent f193ad9f
......@@ -226,7 +226,9 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) {
}
session_id(Crypt::randomBytesBase64());
$this->getMetadataBag()->clearCsrfTokenSeed();
// We set token seed immediately to avoid race condition between two
// simultaneous requests without a seed.
$this->getMetadataBag()->setCsrfTokenSeed(Crypt::randomBytesBase64());
if (isset($old_session_id)) {
$params = session_get_cookie_params();
......
name: CSRF race test
type: module
description: 'Check that CSRF token is generated once.'
package: Testing
version: VERSION
csrf_race_test.csrftoken:
path: '/csrf_race/get_csrf_token/{num}'
defaults:
_controller: '\Drupal\csrf_race_test\Controller\TestController::getCsrfToken'
requirements:
_access: 'TRUE'
csrf_race_test.test:
path: '/csrf_race/test'
defaults:
_controller: '\Drupal\csrf_race_test\Controller\TestController::testMethod'
requirements:
_access: 'TRUE'
<?php
namespace Drupal\csrf_race_test\Controller;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Controller to test concurrent CSRF token generation.
*/
class TestController extends ControllerBase {
/**
* Token generator service.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $tokenGenerator;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('csrf_token')
);
}
/**
* Controller constructor.
*/
public function __construct(CsrfTokenGenerator $token_generator) {
$this->tokenGenerator = $token_generator;
}
/**
* Helper page to load jQuery in test.
*
* @return array
* Empty page with jQuery.
*/
public function testMethod() {
return [
'#markup' => '',
'#attached' => [
'library' => 'core/jquery',
],
];
}
/**
* Just return generated CSRF token for concurrent requests.
*
* We delay the response to the first request to make sure the second request
* is made when the first is not yet finished.
*
* @return \Symfony\Component\HttpFoundation\Response
* CSRF token.
*/
public function getCsrfToken($num) {
sleep($num);
return new JsonResponse($this->tokenGenerator->get());
}
}
<?php
namespace Drupal\FunctionalJavascriptTests\Core;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
/**
* Test race condition for CSRF tokens for simultaneous requests.
*
* @group Session
*/
class CsrfTokenRaceTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['csrf_race_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Test race condition for CSRF tokens for simultaneous requests.
*/
public function testCsrfRace() {
$user = $this->createUser(['access content']);
$this->drupalLogin($user);
$this->drupalGet('/csrf_race/test');
$script = '';
// Delay the request processing of the first request by one second through
// the request parameter, which will simulate the concurrent processing
// of both requests.
foreach ([1, 0] as $i) {
$script .= <<<EOT
jQuery.ajax({
url: "$this->baseUrl/csrf_race/get_csrf_token/$i",
method: "GET",
headers: {
"Content-Type": "application/json"
},
success: function(response) {
jQuery('body').append("<p class='csrf$i'></p>");
jQuery('.csrf$i').html(response);
},
error: function() {
jQuery('body').append('Nothing');
}
});
EOT;
}
$this->getSession()->getDriver()->executeScript($script);
$token0 = $this->assertSession()->waitForElement('css', '.csrf0')->getHtml();
$token1 = $this->assertSession()->waitForElement('css', '.csrf1')->getHtml();
$this->assertNotNull($token0);
$this->assertNotNull($token1);
$this->assertEqual($token0, $token1);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment