diff --git a/core/core.services.yml b/core/core.services.yml index 20b8651..13cb70f 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -407,9 +407,10 @@ services: arguments: ['@state'] csrf_token: class: Drupal\Core\Access\CsrfTokenGenerator - arguments: ['@private_key'] + arguments: ['@private_key', '@settings'] calls: - [setCurrentUser, ['@?current_user']] + - [setRequest, ['@?request'] access_manager: class: Drupal\Core\Access\AccessManager arguments: ['@router.route_provider', '@url_generator', '@paramconverter_manager'] diff --git a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php index 4751f3e..6f649f9 100644 --- a/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php +++ b/core/lib/Drupal/Core/Access/CsrfTokenGenerator.php @@ -8,8 +8,10 @@ namespace Drupal\Core\Access; use Drupal\Component\Utility\Crypt; +use Drupal\Component\Utility\Settings; use Drupal\Core\PrivateKey; use Drupal\Core\Session\AccountInterface; +use Symfony\Component\HttpFoundation\Request; /** * Generates and validates CSRF tokens. @@ -26,6 +28,13 @@ class CsrfTokenGenerator { protected $privateKey; /** + * The settings service. + * + * @var \Drupal\Component\Utility\Settings + */ + protected $settings; + + /** * The current user. * * @var \Drupal\Core\Session\AccountInterface @@ -33,13 +42,23 @@ class CsrfTokenGenerator { protected $currentUser; /** + * The current request. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** * Constructs the token generator. * * @param \Drupal\Core\PrivateKey $private_key * The private key service. + * @param \Drupal\Component\Utility\Settings $settings + * The settings service. */ - public function __construct(PrivateKey $private_key) { + public function __construct(PrivateKey $private_key, Settings $settings) { $this->privateKey = $private_key; + $this->settings = $settings; } /** @@ -53,6 +72,16 @@ public function setCurrentUser(AccountInterface $current_user = NULL) { } /** + * Sets the current request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + */ + public function setRequest(Request $request) { + $this->request = $request; + } + + /** * Generates a token based on $value, the user session, and the private key. * * @param string $value @@ -69,16 +98,18 @@ public function get($value = '') { // 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)) { + if ($this->request->isSecure() && $this->settings->get('mixed_mode_sessions', FALSE)) { $insecure_session_name = substr(session_name(), 1); - if ($this->request->cookies->get($insecure_session_name)) { + if ($this->request->cookies->has($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/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index b52d120..6a4d446 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -846,6 +846,11 @@ public function validateForm($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)) { + \Drupal::formBuilder()->setErrorByName('', $form_state, t('This form must be submitted over a secure connection.')); + } + // Recursively validate each form element. $this->doValidateForm($form, $form_state, $form_id); // After validation, loop through and assign each element its errors. diff --git a/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php b/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php index 5b823a2..a8a6c98 100644 --- a/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\Core\Access { +use Drupal\Component\Utility\Settings; use Drupal\Tests\UnitTestCase; use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Component\Utility\Crypt; @@ -48,7 +49,12 @@ function setUp() { ->method('get') ->will($this->returnValue($this->key)); - $this->generator = new CsrfTokenGenerator($private_key); + $settings = new Settings(array()); + + $this->generator = new CsrfTokenGenerator($private_key, $settings); + + $request = new Request(); + $this->generator->setRequest($request); } /** @@ -58,6 +64,9 @@ public function testGet() { $this->assertInternalType('string', $this->generator->get()); $this->assertNotSame($this->generator->get(), $this->generator->get($this->randomName())); $this->assertNotSame($this->generator->get($this->randomName()), $this->generator->get($this->randomName())); + + // @todo Test code paths using Request::isSecure, settings, and cookie + // attributes. } /**