पाठ 15 / 25
Security: XSS, CSRF, SQL Injection and Passwords
Defend PHP applications against the most common web vulnerabilities.
The essential defences
Most PHP security incidents come from a handful of mistakes. Cross-site scripting (XSS): user data printed into HTML without escaping can run attackers' JavaScript; escape output with htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') or use a templating engine that escapes automatically (Twig, Blade), and add a Content-Security-Policy header. SQL injection: never concatenate input into SQL; use prepared statements with bound parameters. Cross-site request forgery (CSRF): state-changing forms need an unpredictable per-session token checked on submission (frameworks do this), plus SameSite cookies. Passwords: store only hashes made with password_hash() (bcrypt by default, or PASSWORD_ARGON2ID) and check with password_verify(); never use md5 or sha1 for passwords. Other essentials: validate uploaded files by content type and size and store them outside the web root; avoid eval, unserialize on untrusted data and shell commands built from input (escapeshellarg if unavoidable); generate secrets with random_bytes(); keep PHP and dependencies updated (composer audit).
Escaping, CSRF tokens and password hashing
Each defence is a small, consistent habit.
<?php
declare(strict_types=1);
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
// XSS: escape every value printed into HTML
echo '<p>Welcome, ' . e($user['name']) . '</p>';
// CSRF: issue a token per session and verify it on POST
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
echo '<input type="hidden" name="csrf" value="' . e($_SESSION['csrf']) . '">';
if ($_SERVER['REQUEST_METHOD'] === 'POST'
&& !hash_equals($_SESSION['csrf'] ?? '', (string) ($_POST['csrf'] ?? ''))) {
http_response_code(419);
exit('Invalid form token');
}
// passwords: hash on sign-up, verify on login, rehash when the algorithm changes
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);
if (password_verify($attempt, $hash)) {
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$hash = password_hash($attempt, PASSWORD_DEFAULT); // store the upgraded hash
}
}Compare secrets with hash_equals
Comparing tokens with === can leak information through timing differences. hash_equals() compares in constant time, which is the right tool for CSRF tokens, API signatures and similar secrets.
त्वरित जाँच: How should passwords be stored in a PHP application?
- Plain text in the database
- As md5 hashes
- As hashes from password_hash(), checked with password_verify()
- Encrypted with a key stored in the same database
Answer
As hashes from password_hash(), checked with password_verify() — password_hash uses slow, salted algorithms designed for passwords.