Fix #4874
diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php
index 5aac12f..ea78531 100644
--- a/system/libraries/Session/Session.php
+++ b/system/libraries/Session/Session.php
@@ -318,35 +318,80 @@
 		ini_set('session.use_cookies', 1);
 		ini_set('session.use_only_cookies', 1);
 
+		$this->_configure_sid_length();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Configure session ID length
+	 *
+	 * To make life easier, we used to force SHA-1 and 4 bits per
+	 * character on everyone. And of course, someone was unhappy.
+	 *
+	 * Then PHP 7.1 broke backwards-compatibility because ext/session
+	 * is such a mess that nobody wants to touch it with a pole stick,
+	 * and the one guy who does, nobody has the energy to argue with.
+	 *
+	 * So we were forced to make changes, and OF COURSE something was
+	 * going to break and now we have this pile of shit. -- Narf
+	 *
+	 * @return	void
+	 */
+	protected function _configure_sid_length()
+	{
 		if (PHP_VERSION_ID < 70100)
 		{
-			if ((int) ini_get('session.hash_function') === 0)
+			$hash_function = ini_get('session.hash_function');
+			if (ctype_digit($hash_function))
+			{
+				if ($hash_function !== '1')
+				{
+					ini_set('session.hash_function', 1);
+					$bits = 160;
+				}
+			}
+			elseif ( ! in_array($hash_function, hash_algos(), TRUE))
 			{
 				ini_set('session.hash_function', 1);
-				ini_set('session.hash_bits_per_character', $bits_per_character = 4);
+				$bits = 160;
 			}
-			else
+			elseif (($bits = strlen(hash($hash_function, 'dummy', false)) * 4) < 160)
 			{
-				$bits_per_character = (int) ini_get('session.hash_bits_per_character');
+				ini_set('session.hash_function', 1);
+				$bits = 160;
 			}
+
+			$bits_per_character = (int) ini_get('session.hash_bits_per_character');
+			$sid_length         = $bits * $bits_per_character;
 		}
-		elseif ((int) ini_get('session.sid_length') < 40 && ($bits_per_character = (int) ini_get('session.sid_bits_per_character')) === 4)
+		else
 		{
-			ini_set('session.sid_length', 40);
+			$bits_per_character = (int) ini_get('session.sid_bits_per_character');
+			$sid_length         = (int) ini_get('session.sid_length');
+			if (($bits = $sid_length * $bits_per_character) < 160)
+			{
+				// Add as many more characters as necessary to reach at least 160 bits
+				$sid_length += (int) ceil((160 % $bits) / $bits_per_character);
+				ini_set('session.sid_length', $sid_length);
+			}
 		}
 
+		// Yes, 4,5,6 are the only known possible values as of 2016-10-27
 		switch ($bits_per_character)
 		{
 			case 4:
-				$this->_sid_regexp = '[0-9a-f]{40,}';
+				$this->_sid_regexp = '[0-9a-f]';
 				break;
 			case 5:
-				$this->_sid_regexp = '[0-9a-v]{40,}';
+				$this->_sid_regexp = '[0-9a-v]';
 				break;
 			case 6:
-				$this->_sid_regexp = '[0-9a-zA-Z,-]{40,}';
+				$this->_sid_regexp = '[0-9a-zA-Z,-]';
 				break;
 		}
+
+		$this->_sid_regexp .= '{'.$sid_length.'}';
 	}
 
 	// ------------------------------------------------------------------------