PHP Security: Sessions, Passwords and CSRF

Three essential pieces before putting a login into production

🗂️ 1. Sessions with $_SESSION

Current session status:

session_id()
985a7ec5857c…
$_SESSION['demo_usuario']
(empty)

Save something in your session

💡 Why does this work across page reloads?

PHP stores your session data on the server and only sends the browser a cookie with an ID ( PHPSESSID). That's why session_start() must be called before printing any HTML: it needs to be able to send that cookie in the headers.

🔒 2. Password hashing: password_hash() / password_verify()

Hash "saved" on the server (actual password: password123):

password_hash('password123', PASSWORD_BCRYPT)
$2y$12$OvSStgG3PAO20VCi/8H1C.EeymT7Y/w/z4W9dnYbl9/eUZZiQ873m

Try to "log in"

💡 Never store passwords in plain text

password_hash() generates a different hash each time (it includes a random "salt" ), even if the password is the same. That's why you never compare passwords with ==: you use password_verify(), which knows how to extract that salt from the hash itself.

🛡️ 3. Protection CSRF with tokens

Current valid token in $_SESSION:

$_SESSION['csrf_token']
47dd493c50d8137418825f38bd973b02

Send the protected form

This form includes the correct hidden token.

💡 What does a CSRF token prevent?

That another website tricks the user's browser into submitting a form to yours without them knowing. Since that secret token lives in the server session and in a hidden field of the legitimate form, an outside attacker can't guess it.

🎯 Key Concepts

Sessions

  • session_start() before any output
  • Data stored on the server, not in the browser

Hashing

  • password_hash() to store
  • password_verify() to check

CSRF

  • Secret token per session
  • Compare with hash_equals(), never with ==

💪 Practical Exercise

Strengthen the system

  1. Add a counter for failed password attempts in $_SESSION and block after 3 attempts.
  2. Regenerate the session_id() with session_regenerate_id(true) right after a successful login.
  3. Add a session cookie with httponly and samesite=Strict in session_set_cookie_params().

Solution 1

$_SESSION['intentos'] = ($_SESSION['intentos'] ?? 0) + 1;
if ($_SESSION['intentos'] >= 3) {
    die(t('Demasiados intentos, inténtalo más tarde.'));
}

Solution 2

if (password_verify($password, $hashGuardado)) {
    session_regenerate_id(true);
    $_SESSION['usuario_id'] = $usuario['id'];
}

Solution 3

session_set_cookie_params([
    'httponly' => true,
    'samesite' => 'Strict',
    'secure'   => true
]);
session_start();