diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index b52d120..6a81eb0 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -843,6 +843,12 @@ public function validateForm($form_id, &$form, &$form_state) {
 
         // Setting this error will cause the form to fail validation.
         $this->setErrorByName('form_token', $form_state, $this->t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
+
+        // Stop here and don't run any further validation handlers, because they
+        // could invoke non-safe operations which opens the door for CSRF
+        // vulnerabilities.
+        $this->validatedForms[$form_id] = TRUE;
+        return;
       }
     }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsTableSelectTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsTableSelectTest.php
index b8df0f1..9ce3fb2 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsTableSelectTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsTableSelectTest.php
@@ -216,6 +216,9 @@ private function formSubmitHelper($form, $edit) {
     $form_state = form_state_defaults();
 
     $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
+    // The form token CSRF protection should not interfere with this test, so we
+    // bypass it by setting the token to FALSE.
+    $form['#token'] = FALSE;
 
     $form_state['input'] = $edit;
     $form_state['input']['form_id'] = $form_id;
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
index 9fa4d39..36f4ac7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
@@ -104,6 +104,9 @@ function testRequiredFields() {
           $form = array();
           $form_state = form_state_defaults();
           $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
+          // The form token CSRF protection should not interfere with this test,
+          // so we bypass it by setting the token to FALSE.
+          $form['#token'] = FALSE;
           $element = $data['element']['#title'];
           $form[$element] = $data['element'];
           $form[$element]['#required'] = $required;
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ValidationTest.php
index 099f3f8..bda5872 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/ValidationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/ValidationTest.php
@@ -65,6 +65,18 @@ function testValidate() {
     $this->drupalPostForm(NULL, array(), 'Save');
     $this->assertNoFieldByName('name', 'Form element was hidden.');
     $this->assertText('Name value: element_validate_access', 'Value for inaccessible form element exists.');
+
+    // Verify that #validate handlers don't run if the CSRF token is invalid.
+    $this->drupalLogin($this->drupalCreateUser());
+    $this->drupalGet('form-test/validate');
+    $edit = array(
+      'name' => 'validate',
+      'form_token' => 'invalid token'
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertNoFieldByName('name', '#value changed by #validate', 'Form element #value was not altered.');
+    $this->assertNoText('Name value: value changed by form_set_value() in #validate', 'Form element value in $form_state was not altered.');
+    $this->assertText('The form has become outdated. Copy any unsaved work in the form below');
   }
 
   /**
