Web application security is one of the most important topics in PHP development. Any visitor can send arbitrary data to the site, so we must protect ourselves against the most common attacks. In this article we look at the basics: input validation, protection against SQL injection and XSS, password handling and secure sessions.
$username = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');
Never Trust Input
The golden rule of web development: always treat data coming from the user as untrusted input. This applies to $_GET, $_POST and $_COOKIE. Before using the input, we always check whether it is what we expect.
// Checking presence and length
if (isset($_POST['name']) && mb_strlen($_POST['name']) <= 50) {
// input is acceptable
}
// Expecting an email address? Validate it
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
// invalid input
}
Protection Against SQL Injection
SQL injection is an attack where the attacker inserts their own SQL commands through input. The protection is a prepared statement with parameter binding — data is never merged with the query as text.
// WRONG: input is placed directly into the query
$query = "SELECT * FROM users WHERE name = '" . $_POST['name'] . "'";
// CORRECT: prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(['name' => $_POST['name']]);
$user = $stmt->fetch();
Protection Against XSS
XSS (Cross-Site Scripting) allows an attacker to inject JavaScript code into the page. This happens when unvalidated input is printed as HTML. Every output of data that came from the user is converted with htmlspecialchars().
// WRONG: printing unvalidated input echo $_GET['search_term']; // CORRECT: the <, > and " characters are converted into entities echo htmlspecialchars($_GET['search_term'], ENT_QUOTES, 'UTF-8');
Storing Passwords
Passwords are never stored in plain text and are not encrypted — they are hashed with a one-way hash function. PHP has a built-in function password_hash(), which adds a random salt automatically and uses the bcrypt algorithm.
// Registration: compute the hash
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT);
// Login: verify the password against the hash
if (password_verify($_POST['password'], $hash)) {
// password is correct
}
Secure Sessions
Sessions link a user's login across page visits. Attackers may try to steal the session identifier, so we set secure cookie flags and change the session ID after login.
session_start();
// Session cookie should not be readable by JavaScript and works only over HTTPS
session_set_cookie_params([
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
// Change the session ID after a successful login
session_regenerate_id(true);
These are the basics that protect you from the most common vulnerabilities. Security is not a one-time task but a habit you follow with every new piece of code.