diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index e0c1271599e79e37d1792d098882f78495eb6b27..03f66bb97721606a1d5a42decfec3e01b501ad44 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -491,7 +491,7 @@ function drupal_valid_http_host($host) {
  */
 function drupal_settings_initialize() {
   // Export these settings.php variables to the global namespace.
-  global $base_url, $databases, $cookie_domain, $drupal_hash_salt, $config_directories, $config;
+  global $base_url, $databases, $cookie_domain, $config_directories, $config;
   $settings = array();
   $config = array();
 
@@ -1662,10 +1662,13 @@ function drupal_get_user_timezone() {
  *   A salt based on information in settings.php, not in the database.
  */
 function drupal_get_hash_salt() {
-  global $drupal_hash_salt, $databases;
-  // If the $drupal_hash_salt variable is empty, a hash of the serialized
-  // database credentials is used as a fallback salt.
-  return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
+  $hash_salt = settings()->get('hash_salt');
+  // This should never happen, as it breaks user logins and many other services.
+  // Therefore, explicitly notify the user (developer) by throwing an exception.
+  if (empty($hash_salt)) {
+    throw new \RuntimeException('Missing $settings[\'hash_salt\'] in settings.php.');
+  }
+  return $hash_salt;
 }
 
 /**
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 68fc46e69e8b2f7f19de12dff1fda95220ecbfe4..928694d9a906a5e449ed49d98c326b1830e3130f 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1255,17 +1255,14 @@ function install_settings_form_submit($form, &$form_state) {
     'value'    => $database,
     'required' => TRUE,
   );
-  $settings['drupal_hash_salt'] = (object) array(
+  $settings['settings']['hash_salt'] = (object) array(
     'value'    => Crypt::randomStringHashed(55),
     'required' => TRUE,
   );
-
   // Remember the profile which was used.
-  $settings['settings'] = array(
-    'install_profile' => (object) array(
-      'value' => $install_state['parameters']['profile'],
-      'required' => TRUE,
-    ),
+  $settings['settings']['install_profile'] = (object) array(
+    'value' => $install_state['parameters']['profile'],
+    'required' => TRUE,
   );
 
   drupal_rewrite_settings($settings);
diff --git a/core/includes/install.inc b/core/includes/install.inc
index fefa9312415f4ccdfeb509a5ec7b73ee5df9e834..c7f5937941056445f33d508e6289c52e37218c9f 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -7,6 +7,7 @@
 
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\Settings;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
 
@@ -199,8 +200,14 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
   }
   // Build list of setting names and insert the values into the global namespace.
   $variable_names = array();
+  $settings_settings = array();
   foreach ($settings as $setting => $data) {
-    _drupal_rewrite_settings_global($GLOBALS[$setting], $data);
+    if ($setting != 'settings') {
+      _drupal_rewrite_settings_global($GLOBALS[$setting], $data);
+    }
+    else {
+      _drupal_rewrite_settings_global($settings_settings, $data);
+    }
     $variable_names['$'. $setting] = $setting;
   }
   $contents = file_get_contents(DRUPAL_ROOT . '/' . $settings_file);
@@ -307,6 +314,14 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
     if (file_put_contents(DRUPAL_ROOT . '/' . $settings_file, $buffer) === FALSE) {
       throw new Exception(t('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
     }
+    else {
+      // In case any $settings variables were written, import them into the
+      // Settings singleton.
+      if (!empty($settings_settings)) {
+        $old_settings = settings()->getAll();
+        new Settings($settings_settings + $old_settings);
+      }
+    }
   }
   else {
     throw new Exception(t('Failed to open %settings. Verify the file permissions.', array('%settings' => $settings_file)));
diff --git a/core/rebuild.php b/core/rebuild.php
index 9ddf054857f4460cef6ee793ae037af599517502..81d9f5570f29736dc779fb19aeb14329e6ea47fe 100644
--- a/core/rebuild.php
+++ b/core/rebuild.php
@@ -24,7 +24,7 @@
 if (settings()->get('rebuild_access', FALSE) ||
   (isset($_GET['token'], $_GET['timestamp']) &&
     ((REQUEST_TIME - $_GET['timestamp']) < 300) &&
-    ($_GET['token'] === Crypt::hmacBase64($_GET['timestamp'], $GLOBALS['drupal_hash_salt']))
+    ($_GET['token'] === Crypt::hmacBase64($_GET['timestamp'], settings()->get('hash_salt')))
   )) {
 
   drupal_rebuild();
diff --git a/core/scripts/rebuild_token_calculator.sh b/core/scripts/rebuild_token_calculator.sh
index 9b948b48ed12568c12e086d6ab31fe4e2473c828..a75c9b68608568d0b03b9f88334380384a7324d8 100755
--- a/core/scripts/rebuild_token_calculator.sh
+++ b/core/scripts/rebuild_token_calculator.sh
@@ -19,6 +19,6 @@
 }
 
 $timestamp = time();
-$token = Crypt::hmacBase64($timestamp, $drupal_hash_salt);
+$token = Crypt::hmacBase64($timestamp, settings()->get('hash_salt'));
 
 print "timestamp=$timestamp&token=$token\n";
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 5642dc5c82a51e583cf2c83f324d69bbdd1e7fee..ad2c404f1334d79b9b7e1f02d90b080ea8e77885 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -213,25 +213,6 @@
  */
 $databases = array();
 
-/**
- * Salt for one-time login links and cancel links, form tokens, etc.
- *
- * This variable will be set to a random value by the installer. All one-time
- * login links will be invalidated if the value is changed. Note that if your
- * site is deployed on a cluster of web servers, you must ensure that this
- * variable has the same value on each server. If this variable is empty, a hash
- * of the serialized database credentials will be used as a fallback salt.
- *
- * For enhanced security, you may set this variable to a value using the
- * contents of a file outside your docroot that is never saved together
- * with any backups of your Drupal files and database.
- *
- * Example:
- *   $drupal_hash_salt = file_get_contents('/home/example/salt.txt');
- *
- */
-$drupal_hash_salt = '';
-
 /**
  * Location of the site configuration files.
  *
@@ -262,6 +243,25 @@
  * @see \Drupal\Component\Utility\Settings::get()
  */
 
+/**
+ * Salt for one-time login links, cancel links, form tokens, etc.
+ *
+ * This variable will be set to a random value by the installer. All one-time
+ * login links will be invalidated if the value is changed. Note that if your
+ * site is deployed on a cluster of web servers, you must ensure that this
+ * variable has the same value on each server.
+ *
+ * For enhanced security, you may set this variable to a value using the
+ * contents of a file outside your docroot that is never saved together
+ * with any backups of your Drupal files and database.
+ *
+ * Example:
+ * @code
+ *   $settings['hash_salt'] = file_get_contents('/home/example/salt.txt');
+ * @endcode
+ */
+$settings['hash_salt'] = '';
+
 /**
  * Access control for update.php script.
  *