Q1. What is SQL injection and how do you stop it?
User input becomes part of the SQL command. Stop it with prepared statements (PDO placeholders) for every value, allow-lists for identifiers, and a least-privilege database user.
Q2. What is XSS and how do you stop it?
Cross-site scripting: untrusted text is printed into a page unescaped, so an attacker’s script runs in visitors’ browsers. Escape on output for the context (htmlspecialchars($t, ENT_QUOTES, "UTF-8") for HTML), use a CSP, set HttpOnly cookies, and sanitise rich HTML with a library.
<?php
echo htmlspecialchars('<img src=x onerror="alert(1)">', ENT_QUOTES, "UTF-8");
<img src=x onerror="alert(1)">
Q3. What is CSRF and how do you stop it?
A malicious site makes a logged-in user’s browser send a request to your site. Stop it with a random per-session token in every state-changing form, checked with hash_equals(), plus SameSite cookies and POST for changes.
Q4. What is the difference between validation, sanitising and escaping?
Validation checks input is acceptable. Sanitising cleans it on the way in. Escaping makes it safe for a particular destination on the way out (HTML, URL, SQL via prepared statements). They are different jobs, and none replaces the others.
Q5. How do you store passwords?
password_hash() and password_verify(). Upgrade with password_needs_rehash(). Never use MD5 or SHA for passwords.
Q6. What is session fixation?
An attacker plants a known session ID. Fix: session_regenerate_id(true) at login, strict mode, cookies only.
Q7. How do you make file uploads safe?
Check the error code and size, detect the real type with finfo, allow-list types, use a random file name and an extension you choose, store outside the web root (or disable script execution), and serve with nosniff. Never trust $_FILES["x"]["name"] or ["type"].
Q8. What is local file inclusion?
Including a file whose path comes from user input (include $_GET["page"]). Use an allow-list that maps keys to files.
Q9. Which security headers do you know?
Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy, and frame-ancestors (or X-Frame-Options) against clickjacking.
Q10. How do you protect secrets?
Environment variables or a config file outside the web root, never in Git, never printed. Rotate a secret that may have leaked.
Q11. What does display_errors have to do with security?
Error messages can reveal paths, queries and versions. Turn display_errors off in production, log errors, and show a friendly page.
Q12. What is an IDOR?
Insecure direct object reference: changing an ID in a URL or form gives access to someone else’s data because the server did not check ownership. Fix: check permission on every request (WHERE id = :id AND user_id = :me).
Q13. How do you prevent timing attacks when comparing secrets?
Use hash_equals($known, $given) instead of ===.
<?php
var_dump(hash_equals("token123", "token123"));
var_dump(hash_equals("token123", "token124"));
bool(true)
bool(false)
Q14. Is HTTPS enough?
No. It protects data in transit. You still need escaping, prepared statements, CSRF protection, authentication and authorisation checks, and safe handling of files and secrets.
Q15. How would you secure a login form?
Hash passwords, one generic error message, equal timing, rate limiting, CSRF token, session_regenerate_id(true), secure cookie flags, HTTPS, and optional two-factor authentication.
Q16. What is unserialize() risk?
Unserializing untrusted data can create objects and trigger code (object injection). Use json_decode() for untrusted data, or pass ["allowed_classes" => false].