diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index adf4c86344db1922abe8bf5c62268e4f6c42a594..fb6ecc03e8952d2c7f6af44dccb4a3fdbd50f8ad 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -679,6 +679,15 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { $form['#method'] = 'get'; } + // GET forms should not use a CSRF token. + if (isset($form['#method']) && $form['#method'] === 'get') { + // Merges in a default, this means if you've explicitly set #token to the + // the $form_id on a GET form, which we don't recommend, it will work. + $form += [ + '#token' => FALSE, + ]; + } + // Generate a new #build_id for this form, if none has been set already. // The form_build_id is used as key to cache a particular build of the form. // For multi-step forms, this allows the user to go back to an earlier diff --git a/core/modules/search/src/Form/SearchBlockForm.php b/core/modules/search/src/Form/SearchBlockForm.php index 2345c80cad537fa5c99cf31b01feb1392ac8a1ad..243c6413880e89e474afcef4ecae0528dbf879cd 100644 --- a/core/modules/search/src/Form/SearchBlockForm.php +++ b/core/modules/search/src/Form/SearchBlockForm.php @@ -89,7 +89,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { $route = 'search.view_' . $entity_id; $form['#action'] = $this->url($route); - $form['#token'] = FALSE; $form['#method'] = 'get'; $form['keys'] = array( diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php index 389d23efe27fa647d338cc400bd9b6c19aadec85..3ce775d424cf5df3ef3ee7338d858c44bce870d8 100644 --- a/core/modules/system/src/Tests/Form/FormTest.php +++ b/core/modules/system/src/Tests/Form/FormTest.php @@ -294,6 +294,18 @@ public function testInputWithInvalidToken() { $this->assertFieldByName('url', $edit['url']); } + /** + * CSRF tokens for GET forms should not be added by default. + */ + public function testGetFormsCsrfToken() { + // We need to be logged in to have CSRF tokens. + $account = $this->createUser(); + $this->drupalLogin($account); + + $this->drupalGet(Url::fromRoute('form_test.get_form')); + $this->assertNoRaw('form_token'); + } + /** * Tests validation for required textfield element without title. * diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml index a37990bdc284085218f824392e4fe52188370b0d..d8c5833eb9786572ad0f16f7fe51f53f758248a8 100644 --- a/core/modules/system/tests/modules/form_test/form_test.routing.yml +++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml @@ -473,3 +473,10 @@ form_test.form_storage_page_cache: _title: 'Form storage with page cache test' requirements: _access: 'TRUE' + +form_test.get_form: + path: '/form-test/get-form' + defaults: + _form: '\Drupal\form_test\Form\FormTestGetForm' + requirements: + _access: 'TRUE' diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php new file mode 100644 index 0000000000000000000000000000000000000000..9bcb55e2f64ffb25ebeb595612be35c8bc3b7426 --- /dev/null +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestGetForm.php @@ -0,0 +1,44 @@ + 'submit', + '#value' => 'Save', + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + drupal_set_message('The form_test_get_form form has been submitted successfully.'); + } + +} diff --git a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php index 5df546513efbc039958a2e3c8bc911110c97ef00..8906cfda35d078504c35f09314115bd85abb852d 100644 --- a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php +++ b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php @@ -232,7 +232,6 @@ public function testExposedSortAndItemsPerPage() { 'entity_test_view_grants', 'theme', 'url.query_args', - 'user.roles:authenticated', 'languages:language_content' ]; diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index d95e3bcb6f64fbdca4d6bca8357411248c21cb5c..179b7baae162bee95599293b24c4acaf2a4f03ce 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -773,7 +773,7 @@ public function providerTestInvalidToken() { * * @dataProvider providerTestFormTokenCacheability */ - function testFormTokenCacheability($token, $is_authenticated, $expected_form_cacheability, $expected_token_cacheability) { + public function testFormTokenCacheability($token, $is_authenticated, $expected_form_cacheability, $expected_token_cacheability, $method) { $user = $this->prophesize(AccountProxyInterface::class); $user->isAuthenticated() ->willReturn($is_authenticated); @@ -782,6 +782,7 @@ function testFormTokenCacheability($token, $is_authenticated, $expected_form_cac $form_id = 'test_form_id'; $form = $form_id(); + $form['#method'] = $method; if (isset($token)) { $form['#token'] = $token; @@ -797,7 +798,7 @@ function testFormTokenCacheability($token, $is_authenticated, $expected_form_cac $form_state = new FormState(); $built_form = $this->formBuilder->buildForm($form_arg, $form_state); - if (!isset($expected_form_cacheability)) { + if (!isset($expected_form_cacheability) || ($method == 'get' && !is_string($token))) { $this->assertFalse(isset($built_form['#cache'])); } else { @@ -820,10 +821,12 @@ function testFormTokenCacheability($token, $is_authenticated, $expected_form_cac */ function providerTestFormTokenCacheability() { return [ - 'token:none,authenticated:true' => [NULL, TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0]], - 'token:false,authenticated:true' => [FALSE, TRUE, NULL, NULL], - 'token:none,authenticated:false' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL], - 'token:false,authenticated:false' => [FALSE, FALSE, NULL, NULL], + 'token:none,authenticated:true' => [NULL, TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0], 'post'], + 'token:none,authenticated:false' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL, 'post'], + 'token:false,authenticated:false' => [FALSE, FALSE, NULL, NULL, 'post'], + 'token:false,authenticated:true' => [FALSE, TRUE, NULL, NULL, 'post'], + 'token:none,authenticated:false,method:get' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL, 'get'], + 'token:test_form_id,authenticated:false,method:get' => ['test_form_id', TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0], 'get'], ]; }