// Vulnerable: Missing or inappropriate SameSite attribute
public function login(Request $request) {
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$token = $request->user()->createToken('auth')->plainTextToken;
// Dangerous: No SameSite attribute
return response('Login successful')->cookie(
'auth_token',
$token,
60 * 24, // 24 hours
'/',
null,
true, // secure
true // httpOnly
// Missing SameSite!
);
}
}
// Another vulnerable pattern
public function setUserPreference(Request $request) {
$theme = $request->input('theme');
// Problematic: SameSite=None without proper justification
Cookie::queue(
'user_theme',
$theme,
60 * 24 * 30,
'/',
null,
true,
false,
false,
'None' // Dangerous: Too permissive
);
}
// config/session.php (missing SameSite)
return [
'same_site' => null, // Problematic: No CSRF protection
];
// Secure: Proper SameSite configuration
public function login(Request $request) {
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$user = $request->user();
$token = $user->createToken('auth')->plainTextToken;
// Secure: Strict SameSite for authentication
return response('Login successful')->cookie(
'auth_token',
$token,
60 * 24, // 24 hours
'/',
config('session.domain'),
true, // secure
true, // httpOnly
false,
'Strict' // Strict for auth cookies
);
}
return response('Invalid credentials', 401);
}
// Different SameSite values for different use cases
public function setUserPreference(Request $request) {
$theme = $request->input('theme');
// Validate input
$allowedThemes = ['light', 'dark', 'auto'];
if (!in_array($theme, $allowedThemes)) {
return response()->json(['error' => 'Invalid theme'], 400);
}
// Secure: Lax for non-critical preferences
Cookie::queue(
'user_theme',
$theme,
60 * 24 * 30, // 30 days
'/',
config('session.domain'),
true, // secure
false, // httpOnly (false for JS access to theme)
false,
'Lax' // Lax allows some cross-site usage
);
return response()->json(['status' => 'theme_saved']);
}
// Service for managing SameSite policies
class CookieSameSiteService {
public function getSameSiteForCookieType($cookieType) {
$policies = [
'auth' => 'Strict', // Authentication cookies
'session' => 'Strict', // Session cookies
'csrf' => 'Strict', // CSRF tokens
'preferences' => 'Lax', // User preferences
'analytics' => 'Lax', // Analytics cookies
'tracking' => 'None', // Only if absolutely necessary
];
return $policies[$cookieType] ?? 'Lax';
}
public function setCookieSecurely($name, $value, $type, $minutes = 60) {
$sameSite = $this->getSameSiteForCookieType($type);
// For SameSite=None, Secure flag is required
$secure = $sameSite === 'None' ? true : config('session.secure', true);
return Cookie::make(
$name,
$value,
$minutes,
'/',
config('session.domain'),
$secure,
true, // httpOnly for security
false,
$sameSite
);
}
}
// Middleware to validate cookie security
class CookieSecurityMiddleware {
public function handle($request, Closure $next) {
$response = $next($request);
// Validate response cookies
foreach ($response->headers->getCookies() as $cookie) {
$this->validateCookieSecurity($cookie);
}
return $response;
}
private function validateCookieSecurity($cookie) {
$name = $cookie->getName();
$sameSite = $cookie->getSameSite();
// Check for missing SameSite
if (empty($sameSite)) {
Log::warning('Cookie without SameSite attribute', ['cookie' => $name]);
}
// Validate SameSite=None has Secure flag
if ($sameSite === 'None' && !$cookie->isSecure()) {
Log::error('SameSite=None cookie without Secure flag', ['cookie' => $name]);
}
// Check sensitive cookies have Strict SameSite
$sensitiveCookies = ['auth_token', 'session_token', 'csrf_token'];
if (in_array($name, $sensitiveCookies) && $sameSite !== 'Strict') {
Log::warning('Sensitive cookie without Strict SameSite', [
'cookie' => $name,
'samesite' => $sameSite
]);
}
}
}
// config/session.php (secure configuration)
return [
'driver' => env('SESSION_DRIVER', 'file'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => false,
'files' => storage_path('framework/sessions'),
'connection' => env('SESSION_CONNECTION', null),
'table' => 'sessions',
'store' => env('SESSION_STORE', null),
'lottery' => [2, 100],
'cookie' => env('SESSION_COOKIE', 'laravel_session'),
'path' => '/',
'domain' => env('SESSION_DOMAIN', null),
'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => env('SESSION_SAME_SITE', 'Strict'), // Secure default
];