Validating default PHP session ID values
I recently needed to validate the value created by PHP for its session ID. After a bit of research, I realised that there are two interesting php.ini config settings that relate to this value:
- session.sid_length is the number of characters in the ID
- session.sid_bits_per_character controls the set of characters used. From the manual:
The possible values are ‘4’ (0-9, a-f), ‘5’ (0-9, a-v), and ‘6’ (0-9, a-z, A-Z, “-“, “,”).
Therefore, to validate the session ID we need to create a regular expression that looks for the correct set of characters of the expected length.
I wrote function to do this:
function isValidSessionId(string $sessionId): bool
{
$sidLength = ini_get('session.sid_length');
switch (ini_get('session.sid_bits_per_character')) {
case 6:
$characterClass = '0-9a-zA-z,-';
break;
case 5:
$characterClass = '0-9a-v';
break;
case 4:
$characterClass = '0-9a-f';
break;
default:
throw new \RuntimeException('Unknown value in session.sid_bits_per_character.');
}
$pattern = '/^[' . $characterClass . ']{' . $sidLength . '}$/';
return preg_match($pattern, $sessionId) === 1;
}
You could use it like this:
$name = session_name();
if (isset($_COOKIE[$name])) {
if (!isValidSessionId($_COOKIE[$name])) {
// invalid - return an error, just send back a 500 or something
exit;
}
}
As far as I can tell, we can’t use session_id() as we haven’t started the session yet, however as the session is just a cookie at the HTTP level, we can use $_COOKIE instead.
Note also that the manual has an excellent section on Sessions and Security which is worth reading.
This seems interesting. But I'm curious, what was the scenario where you needed to validate a session ID? I couldn't think of anything right off the top of head that fit that need.
Thanks
Yes, it looks like it's only checking if the characters in the session id appear valid, not whether the session id itself is valid.
Rather than using switch/case, you could use a look-up table in your isValidSessionId() function, like:
function isValidSessionId(string $sessionId): bool
{
if (empty($sessionId)) {
return false;
}
$sidLength = ini_get('session.sid_length');
$bitsPerCharacter = ini_get('session.sid_bits_per_character');
$characterClass = [
6 => '0-9a-zA-z,-',
5 => '0-9a-z',
4 => '0-9a-f'
];
if (array_key_exists($bitsPerCharacter, $characterClass)) {
$pattern = '/^[' . $characterClass . ']{' . $sidLength . '}$/';
return preg_match($pattern, $sessionId) === 1;
}
throw new \RuntimeException('Unknown value in session.sid_bits_per_character.');
}
Sorry – typo I've just missed, the $pattern string should be:
$pattern = '/^[' . $characterClass[$bitsPerCharacter] . ']{' . $sidLength . '}$/';
The documentation shows a character set of "0-9a-v" for 5 bits per character. The example code uses "0-9a-z" – just in case anyone copies the code as is you'll need to make that change.
https://www.php.net/manual/en/session.configuration.php#ini.session.sid-bits-per-character
Also, great post. Thanks for the insight :)
Fixed! Thanks.