diff --git a/core/includes/form.inc b/core/includes/form.inc index 29b6c2d..ef41c4e 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -1211,6 +1211,11 @@ function drupal_validate_form($form_id, &$form, &$form_state) { } } + // Ensure the correct protocol when #https is set. + if (!empty($form['#https']) && !\Drupal::request()->isSecure() && settings()->get('mixed_mode_sessions', FALSE)) { + form_set_error('', t('This form must be submitted over a secure connection.')); + } + _form_validate($form, $form_state, $form_id); $validated_forms[$form_id] = TRUE; diff --git a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php index 910a77b..93ad32d 100644 --- a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php +++ b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php @@ -66,7 +66,20 @@ public function setRequest(Request $request) { * @see drupal_get_hash_salt() */ public function get($value = '') { - return Crypt::hmacBase64($value, session_id() . $this->privateKey->get() . drupal_get_hash_salt()); + // For mixed HTTP(S) sessions, use a constant identifier so that tokens can + // be shared between protocols. + $identifier = NULL; + if ($this->request->isSecure() && settings()->get('mixed_mode_sessions', FALSE)) { + $insecure_session_name = substr(session_name(), 1); + if ($this->request->cookies->get($insecure_session_name)) { + $identifier = $this->request->cookies->get($insecure_session_name); + } + } + // Otherwise, use the session ID. + if (!empty($identifier)) { + $identifier = session_id(); + } + return Crypt::hmacBase64($value, $identifier . $this->privateKey->get() . drupal_get_hash_salt()); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php b/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php index 87cd97b..b99c843 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php @@ -149,13 +149,23 @@ protected function testHttpsSession() { $this->drupalGet('user'); $form = $this->xpath('//form[@id="user-login-form"]'); $this->assertEqual(substr($form[0]['action'], 0, 6), 'https:', 'Login form action is secure'); - $form[0]['action'] = $this->httpsUrl('user'); + // Change the form to submit to an insecure URL and make sure the + // submission fails. + $form[0]['action'] = $this->httpUrl('user'); $edit = array( 'name' => $user->getUsername(), 'pass' => $user->pass_raw, ); $this->drupalPostForm(NULL, $edit, t('Log in')); + $this->assertText(t('This form must be submitted over a secure connection.'), 'Form submission failed over HTTP.'); + + // Now try with a secure URL and make sure the submission succeeds. + $form[0]['action'] = $this->httpsUrl('user'); + $this->drupalPost(NULL, $edit, t('Log in')); + $this->assertNoText(t('This form must be submitted over a secure connection.'), 'Form submission succeeded over HTTPS.'); + $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $user->name))); + // Check secure cookie on secure page. $this->assertTrue($this->cookies[$secure_session_name]['secure'], 'The secure cookie has the secure attribute'); // Check insecure cookie on secure page. @@ -211,6 +221,20 @@ protected function testHttpsSession() { // Test that the user is also authenticated on the insecure site. $this->drupalGet("user/" . $user->id() . "/edit"); $this->assertResponse(200); + + // Test that tokens can be shared in mixed mode. + $this->settingsSet('mixed_mode_sessions', TRUE); + $this->drupalGet($this->httpUrl('session-test/shared-token')); + $matches = array(); + preg_match('/\s*sharedToken:(.*)\n/', $this->drupalGetContent(), $matches); + $this->assertTrue(!empty($matches[1]) , 'Found shared token on the HTTP URL.'); + $token_plain = $matches[1]; + $this->drupalGet($this->httpsUrl('session-test/shared-token')); + $matches = array(); + preg_match('/\s*sharedToken:(.*)\n/', $this->drupalGetContent(), $matches); + $this->assertTrue(!empty($matches[1]) , 'Found shared token on the HTTPS URL.'); + $token_secure = $matches[1]; + $this->assertEqual($token_plain, $token_secure, 'Tokens are shared in mixed HTTPS sessions.'); } /** diff --git a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/Controller/SessionTestController.php b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/Controller/SessionTestController.php index 6b00515..30af2dd 100644 --- a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/Controller/SessionTestController.php +++ b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/Controller/SessionTestController.php @@ -135,4 +135,14 @@ public function setNotStarted() { public function isLoggedIn() { return $this->t('User is logged in.'); } + + /** + * Display the result of Drupal::csrfToken()->get(). + * + * @return string + * A notification message with a CSRF token. + */ + public function sharedToken() { + return 'sharedToken:' . \Drupal::csrfToken()->get($value); + } } diff --git a/core/modules/system/tests/modules/session_test/session_test.routing.yml b/core/modules/system/tests/modules/session_test/session_test.routing.yml index 1e7b127..4f250ec 100644 --- a/core/modules/system/tests/modules/session_test/session_test.routing.yml +++ b/core/modules/system/tests/modules/session_test/session_test.routing.yml @@ -75,3 +75,11 @@ session_test.is_logged_in: _controller: '\Drupal\session_test\Controller\SessionTestController::isLoggedIn' requirements: _user_is_logged_in: 'TRUE' + +session_test.shared_token: + path: '/session-test/shared-token' + defaults: + _title: 'Test that tokens can be shared in mixed mode' + _controller: '\Drupal\session_test\Controller\SessionTestController::sharedToken' + requirements: + _permission: 'access content'