Overview
Broken Function Level Authorization (BFLA) vulnerabilities occur when an application uses token-based checks for access control but relies on non-constant-time comparisons, enabling timing-based information exposure. CVE-2017-14775 documents such a flaw in Laravel before 5.5.10, where the remember_me token verification performed by the DatabaseUserProvider did not use a constant-time comparison, creating a CWE-200 information exposure risk that could aid token guessing and unauthorized access.
Impact and exploitation: An attacker who can observe requests involving the remember_me cookie could measure tiny timing differences when comparing tokens and gradually narrow the space of possible tokens, potentially impersonating a user without reauthenticating. This undermines the trust boundary between authenticated and unauthenticated requests and can be used to bypass login controls.
Fix approach: Upgrade Laravel to 5.5.10 or later (or apply the patched DatabaseUserProvider) so that remember_token verification uses a constant-time comparison such as hash_equals. In PHP, avoid direct string equality and replace with hash_equals, after ensuring the PHP version supports it. Also rotate remember_token values after login or when tokens may have been compromised.
Additional mitigations: use HTTPS for all sessions, mark cookies Secure and HttpOnly, set reasonable remember_me lifetimes, keep dependencies up to date, and add tests to verify that token comparisons are performed in constant time and that remember-me tokens are properly invalidated on rotation or logout.
Affected Versions
Laravel <= 5.5.9 (before 5.5.10)
Code Fix Example
Laravel API Security Remediation
<?php
// Demonstration of vulnerable token check and a constant-time fix
class User {
public $remember_token;
public function __construct($token) {
$this->remember_token = $token;
}
}
function verifyRememberTokenVulnerable($providedToken, $user) {
// Vulnerable: non-constant-time comparison
if ($providedToken !== $user->remember_token) {
return false;
}
return true;
}
function verifyRememberTokenFixed($providedToken, $user) {
// Fixed: constant-time comparison using hash_equals
if (!is_string($providedToken) || !is_string($user->remember_token)) {
return false;
}
return hash_equals($user->remember_token, $providedToken);
}
// Demo
$user = new User('abcd1234efgh5678');
$provided = 'abcd1234efgh5678';
var_dump(verifyRememberTokenVulnerable($provided, $user));
var_dump(verifyRememberTokenFixed($provided, $user));
?>