Session Regeneration

Session ID Regeneration and Session Blocking

A new ID at each privilege change defeats fixation (Session Fixation); Auth::login() calls regenerate(true) for you. Mind the argument: plain regenerate() keeps the old record, and the sessions table here still held the old row afterwards. regenerate(true) deletes it; invalidate() also empties the data, and with regenerateToken() it is logout.

PHP's file handler locks the session file (Session Storage Handlers), but Laravel 2,157 takes no lock: each request writes back the array it loaded, so the slower of two overlapping requests wins. block() serializes requests that share a session, using a cache lock (Atomic Locks):

routes/web.php: one writer, with and without block()PHP
$add = function (Request $request, string $sku) {
    sleep($request->integer('wait'));            // a slow payment-gateway call
    $request->session()->push('cart.items', $sku);
};
Route::get('/add/{sku}', $add);
Route::get('/b/add/{sku}', $add)->block(lockSeconds: 10, waitSeconds: 10);
Route::get('/cart/items', fn () => session('cart.items'));
A slow and a fast request from one browser sessionPHP
for p in '' b/; do
  rm -f jar; curl -s -c jar $B/cart > /dev/null
  curl -s -b jar -o /dev/null -w "slow %{time_total}s\n" "$B/${p}add/SLOW?wait=2" &
  sleep 0.3
  curl -s -b jar -o /dev/null -w "fast %{time_total}s\n" $B/${p}add/FAST; wait
  curl -s -b jar $B/cart/items; echo
done
Output
fast 0.022306s
slow 2.028357s
["SLOW"]
slow 2.049021s
fast 1.795837s
["SLOW","FAST"]

Unblocked, FAST was saved and then overwritten. Blocked, it waited 1.8 s and both items survived; past waitSeconds it would throw LockTimeoutException. Block only routes that write.