Overview
The CVE-2017-14775 vulnerability affected Laravel before 5.5.10, where the remember_me token verification in DatabaseUserProvider did not use constant-time comparison. This allowed timing-based information disclosure (CWE-200) that could enable an attacker to brute-force the remember_token and hijack a user session. In real-world terms, a persistent remember-me cookie could be exploited to gain unauthorized access without a password if an attacker could observe or influence the token verification timing. The issue hinges on non-constant-time string comparison during token validation, which can leak information about the token bits through response timing differences. While tokens are random, the imperfect comparison provides a potential side channel that skilled attackers could exploit across repeated requests. This vulnerability is categorized under CWE-200 for information exposure due to improper cryptographic handling and improper token verification.
Affected Versions
Laravel 5.5.x before 5.5.10
Code Fix Example
Laravel API Security Remediation
<?php
// Demonstration of vulnerable vs fixed remember_token verification in Laravel-like code.
class VulnerableDatabaseUserProvider
{
public function retrieveByToken($identifier, $token)
{
$user = $this->getUserById($identifier);
if (!$user) { return null; }
// Vulnerable: non-constant-time comparison using loose equality
if ($token == $user->remember_token) {
return $user;
}
return null;
}
protected function getUserById($id)
{
$users = [
1 => (object)[ 'id' => 1, 'remember_token' => 'abcd1234efgh5678ijkl9012mnop3456' ],
];
return $users[$id] ?? null;
}
}
class FixedDatabaseUserProvider
{
public function retrieveByToken($identifier, $token)
{
$user = $this->getUserById($identifier);
if (!$user) { return null; }
// Fixed: use constant-time comparison
if (hash_equals((string)$user->remember_token, (string)$token)) {
return $user;
}
return null;
}
protected function getUserById($id)
{
$users = [
1 => (object)[ 'id' => 1, 'remember_token' => 'abcd1234efgh5678ijkl9012mnop3456' ],
];
return $users[$id] ?? null;
}
}
// Demonstration (simulated cookies and identifiers)
$cookieToken = isset($_COOKIE['remember_token']) ? $_COOKIE['remember_token'] : '';
$identifier = 1; // simulated user id
$vul = (new VulnerableDatabaseUserProvider())->retrieveByToken($identifier, $cookieToken);
$fix = (new FixedDatabaseUserProvider())->retrieveByToken($identifier, $cookieToken);
echo $vul ? 'VULNERABLE: token matched' : 'VULNERABLE: no match';
echo PHP_EOL;
echo $fix ? 'FIXED: token matched' : 'FIXED: no match';
?>