Forms, sessions and safety

Handling GET and POST, validating input, keeping sessions, and the defences against the classic web attacks.

Reading request data

$email = filter_input(INPUT_POST, "email", FILTER_VALIDATE_EMAIL);
$page  = filter_input(INPUT_GET, "page", FILTER_VALIDATE_INT, ["options" => ["min_range" => 1]]);

if ($email === null || $email === false) {
    http_response_code(422);
    exit("Invalid email");
}

// never: $email = $_POST["email"]; then use it directly in SQL or HTML
SuperglobalCarries
$_GETQuery-string parameters
$_POSTSubmitted form body
$_SESSIONPer-visitor server-side state
$_COOKIECookie values from the browser
$_SERVERRequest and server metadata
⚠️
Validate, do not sanitise blindly. Decide what a valid value looks like (an integer, an email, a value from an allow-list) and reject everything else.

Sessions and authentication

session_start();

// login
$user = findUserByEmail($email);
if ($user && password_verify($password, $user["password_hash"])) {
    session_regenerate_id(true);        // prevent session fixation
    $_SESSION["uid"] = $user["id"];
}

// protection
if (empty($_SESSION["uid"])) {
    header("Location: /login");
    exit;
}

// hashing a new password
$hash = password_hash($password, PASSWORD_DEFAULT);
  • password_hash / password_verify handle salting and cost factors for you.
  • Regenerate the session id on privilege change to defeat session fixation.
  • Set cookie flags: HttpOnly, Secure, SameSite=Lax.

The classic attacks and their fixes

AttackDefence
SQL injectionPrepared statements with bound parameters
XSShtmlspecialchars() on every output
CSRFPer-session token in forms, verified on POST
File upload abuseCheck MIME type and size, store outside the web root, rename
Path traversalNever concatenate user input into a filesystem path
// SQL injection, fixed
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();

// CSRF token
$_SESSION["csrf"] = bin2hex(random_bytes(32));
// in the form: <input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
// on submit:   hash_equals($_SESSION["csrf"], $_POST["csrf"] ?? "")
⚠️
Compare CSRF tokens with hash_equals(), not == — constant-time comparison avoids leaking information through timing.

FAQ

Where should uploaded files go?
Outside the web root, with a generated name and a recorded whitelist of allowed types. Never trust the client-supplied filename or MIME type.
Why not use md5 or sha1 for passwords?
They are fast and unsalted, so a GPU can test billions of guesses per second. Use password_hash() with bcrypt or Argon2.

PHP arrays Hash functions: MD5, SHA-1, SHA-256

Last refreshed 2026-09-17.