Web Security Interview Questions and Answers
Q21. XSS in HTML Attributes
Q21. User input is reflected inside an HTML attribute: <input value="USER_INPUT">. Write a payload that breaks out of the attribute and executes alert(1).
Answer: You close the attribute and the tag, then add a new element with an event handler. A working payload is: "><img src=x onerror=alert(1)>. The "> ends the value and the input tag, and the broken image fires onerror. An attribute-only variant is " onfocus=alert(1) autofocus=", which adds an event handler without leaving the tag.
Q22. Bypassing Script Tag Filters
Q22. Suppose a site filters out the <script> tag. Provide one XSS payload that still works using a different mechanism.
Answer: You do not need a <script> tag because event handlers also run JavaScript. A reliable payload is <img src=x onerror=alert(1)>, which executes when the broken image fails to load. An SVG version is <svg onload=alert(1)>. A link-based version uses a javascript:alert(1) URL in an href. These show that filtering one tag is not real XSS protection.
Q23. DOM-Based XSS Mitigation
Q23. Given document.getElementById('result').innerHTML = location.hash.substring(1);: what type of XSS is this, and how would you rewrite it safely?
Answer: This is DOM-based XSS, because untrusted data from location.hash is written straight into the page by client-side JavaScript. Using innerHTML lets that data be parsed as HTML, so a payload like #<img src=x onerror=alert(1)> runs. The safe fix is to use textContent instead of innerHTML, which inserts the value as plain text. If HTML is truly required, sanitize it with DOMPurify first.
Q24. Testing for Reflected XSS
Q24. When testing a parameter for reflected XSS, what are the first two checks you do? Briefly explain why finding the input’s context matters.
Answer: First, send a harmless unique marker (like xyz123) and check whether and where it is reflected in the response. Second, test which special characters survive unencoded (such as < > " '). Finding the context matters because the same input is dangerous in different ways in an HTML body, an attribute, or a JS string, and each needs a different breakout. Knowing the context tells you exactly which characters you must inject for a working payload.
Q25. Testing for Stored XSS
Q25. How would you test a comment system for stored XSS? Briefly describe what payload you would submit first, and how to confirm it is stored.
Answer: First, submit a benign probe such as <b>test123</b> or a unique marker to see if the input is rendered as HTML. To confirm it is truly stored and not just reflected, reload the page in a fresh session or different browser and check whether the payload still appears and executes. If it runs for a different user without resubmitting, it is stored XSS. Only after confirming context would you escalate to something like <img src=x onerror=alert(1)> in an authorized test.
Q26. Hardening CSP Policies
Q26. Given the CSP script-src 'self' 'unsafe-inline': briefly explain why this policy still permits XSS, and suggest one change that would harden it.
Answer: The 'unsafe-inline' value allows inline scripts and inline event handlers to execute, which is precisely what most XSS payloads rely on. So even though external scripts are limited to your own origin, an injected onerror=alert(1) or inline <script> still runs. To harden it, remove 'unsafe-inline' and allow specific inline scripts only via a per-request nonce (or hash), e.g., script-src 'self' 'nonce-RANDOM'. Then injected scripts without the correct nonce are blocked.
Q27. SQL Injection Prevention
Q27. Given "SELECT * FROM users WHERE username = '" + user + "' AND password = '" + pw + "'": write a value for user that bypasses the password check. Then show the safe version.
Answer: Set user to admin'-- (note the trailing space). The query becomes ... WHERE username = 'admin'-- '; AND password='...', and the -- comments out the password check, logging you in as admin. Another classic is ' OR '1'='1. The safe version uses parameters: SELECT * FROM users WHERE username = ? AND password = ? with both values bound, so the input can never alter the query structure.
Q28. UNION-Based SQL Injection
Q28. On a vulnerable endpoint like /product?id=42, what is a UNION-based SQL injection used for? Show one example payload.
Answer: UNION-based injection appends a second SELECT so its results are merged into the original output, letting you pull data from other tables. First, you find the column count, e.g., 42 ORDER BY 3-- until it errors, or 42 UNION SELECT NULL,NULL,NULL--. Once matched, you extract data: 42 UNION SELECT username,password,NULL FROM users--. You can also read metadata via information_schema.tables to discover table names.
Q29. Blind SQL Injection Detection
Q29. An endpoint reflects no data and returns no errors, but you suspect SQL injection. Write one detection payload and say what observable signal would confirm it.
Answer: Use a time-based payload, since you have no visible output to rely on. For example, 1' AND SLEEP(5)-- (MySQL) or 1'; WAITFOR DELAY '0:0:5'-- (SQL Server). The observable signal is the response taking about 5 seconds longer than a normal request. If the delay reliably appears only when the condition is true, the injection is confirmed.
Q30. Parameterizing ORDER BY
Q30. Why can’t you parameterize an ORDER BY column_name clause with a prepared statement? How would you safely accept a user-supplied sort column?
Answer: Parameters can only stand in for data values, not for identifiers like column or table names or SQL keywords. ORDER BY needs an actual column name, which is part of the query structure, so a placeholder there is invalid. The safe approach is an allowlist: map the user’s choice to a fixed set of permitted columns in your code and reject anything else. For example, only accept ‘name’ or ‘date’ and ignore any other value.
Q31. ORM and SQL Injection
Q31. Given db.session.query(User).filter(text(f"username = '{user}'")).first(): is this code safe just because it uses an ORM? Briefly explain, and show one safer way.
Answer: No, it is not safe. Using an ORM does not help here because text() with an f-string concatenates raw user input straight into SQL, recreating classic SQL injection. The protection comes from parameter binding, not from the ORM itself. A safer version uses bound parameters: filter(text("username = :u")).params(u=user), or better, the ORM’s own filter: filter(User.username==user).
Q32. Command Injection Prevention
Q32. Given vulnerable Node.js code exec(`ping -c 1 ${userInput}`, callback): write one command injection payload. How would you call the command safely without using a shell?
Answer: Because exec runs through a shell, shell metacharacters break out of the intended command. A payload is 8.8.8.8; id or 8.8.8.8 | id, which runs id after the ping. The safe fix is to avoid the shell entirely by using execFile or spawn with the argument passed as a separate array element: execFile('ping', ['-c', '1', userInput]). Then the input is treated as a single argument, not parsed by a shell.
Q33. Blind Command Injection
Q33. A command injection endpoint returns no output. Suggest one payload you could use to confirm the vulnerability anyway.
Answer: Use a time-delay command, just like blind SQLi detection. A payload such as ; sleep 5 (or | sleep 5) makes the server pause before responding. If the response consistently takes about 5 extra seconds, command execution is confirmed. An out-of-band alternative is forcing the server to ping or DNS-resolve a host you control and watching for the callback.
Q34. Preventing Command Injection
Q34. An app runs md5sum <filename> on a user-supplied filename. How could a malicious filename exploit this? Briefly describe one fix.
Answer: If the filename is passed through a shell, a value like x; rm -rf / or x && id injects extra commands, turning it into command injection. The argument is no longer just a filename but a chunk of shell. The fix is to call the program without a shell and pass the filename as a single argument (e.g., execFile('md5sum', [filename])). Also, validate the filename against an allowlist of safe characters.
Q35. CSRF Attack Mechanics
Q35. There is a POST endpoint /account/change-email with no CSRF protection. Write a short HTML snippet that, when a logged-in user visits the attacker’s page, changes their email.
Answer: Because the request is a simple form POST and the browser auto-attaches the victim’s session cookie, an attacker page can submit it for them. Example: <form action='https://site.com/account/change-email' method='POST'><input name='email' value='attacker@evil.com'></form><script>document.forms[0].submit()</script>. The script auto-submits it on page load. The defense is an anti-CSRF token and SameSite cookies.
Q36. JSON APIs and CSRF
Q36. An endpoint accepts only Content-Type: application/json. Briefly explain why a plain HTML <form> cannot be used to attack it via CSRF.
Answer: An HTML form can only send three content types: application/x-www-form-urlencoded, multipart/form-data, and text/plain. It cannot set Content-Type: application/json, so a JSON-only endpoint will reject the forged request. To send JSON cross-site you would need JavaScript (fetch/XHR), which is blocked from setting that header cross-origin without a CORS preflight the attacker can’t satisfy. This makes strict JSON-only APIs naturally more resistant to simple form-based CSRF. It should still not be the only defense.
Q37. Testing for CSRF
Q37. How would you test whether a state-changing endpoint is vulnerable to CSRF? Briefly describe two checks.
Answer: First, capture a legitimate request and check whether it includes any unpredictable anti-CSRF token; if there is none, it is a strong candidate. Second, replay the request after removing the token/Referer header (or from a different origin) and see if it still succeeds. You can also check the cookie’s SameSite setting. If the action completes without a per-request secret and works cross-site, it is vulnerable.
Q38. CSRF Defenses
Q38. Name two common defenses against CSRF. Briefly say how each one works.
Answer: Anti-CSRF tokens: the server embeds a random, per-session (or per-request) token in the form, and rejects any request that lacks the matching value, which an attacker’s site cannot read or guess. SameSite cookies: setting the session cookie to SameSite=Lax or Strict stops the browser from attaching it to cross-site requests, so the forged request arrives unauthenticated. A third option is checking the Origin/Referer header to confirm the request came from your own site. Using tokens plus SameSite together is best.
Q39. Secure Password Storage
Q39. Outline the secure way to store and verify a password. Name (a) one password-hashing algorithm and (b) why a salt is added.
Answer: On registration, hash the password with a slow, dedicated algorithm such as bcrypt or Argon2 together with a unique random salt, and store only the resulting hash. On login, hash the entered password the same way and compare it to the stored value. (a) The hashing algorithm is bcrypt (or Argon2). (b) A salt is a unique random value added per user so that identical passwords produce different hashes, which defeats precomputed rainbow tables and stops attackers from cracking many accounts at once.
Q40. Session Fixation Attacks
Q40. Briefly describe how a session fixation attack works: how does the attacker get the victim to use a session ID the attacker already knows? Name one server-side fix.
Answer: The attacker first obtains a valid session ID from the site, then plants it in the victim’s browser, for example via a crafted link with the session ID in the URL or by setting a cookie. When the victim logs in, the server keeps that same ID, so it is now authenticated and the attacker (who knows the ID) shares the session. The server-side fix is to regenerate a brand-new session ID immediately after a successful login, so any pre-set ID becomes useless.
Q41. Username Enumeration
Q41. A login form returns ‘Username not found’ (404) when the user does not exist, and ‘Wrong password’ (401) when it does. What is this vulnerability called, and how should the server respond instead?
Answer: This is username enumeration through different responses (and status codes). It lets an attacker tell valid accounts apart from invalid ones, which speeds up targeted password attacks. The server should instead return one identical generic message such as ‘invalid username or password’ with the same status code for both cases. It should also keep the response time consistent so timing cannot reveal which usernames exist.
Q42. Brute-Force Testing
Q42. How would you test a login form for brute-force weakness? Name one tool and one server-side defense.
Answer: You automate many login attempts with varied passwords and watch whether the server keeps accepting attempts without slowing or blocking you. A common tool is Burp Suite Intruder (Hydra is another). On the server side you would expect to find rate limiting and account lockout/throttling after several failed attempts. Additional defenses include CAPTCHA after repeated failures and multi-factor authentication.
Q43. Password Reset Token Leaks
Q43. A password-reset URL looks like GET /reset-password?token=ABC123XYZ. Name two ways this token can leak.
Answer: Because the token is in the URL query string, it ends up in places URLs are recorded. Two leak paths are browser history (and bookmarks) and server/proxy access logs that store full URLs. A third is the Referer header, which can send the URL to any third-party resource loaded on the page. Safer designs use short-lived single-use tokens and avoid putting them in GET URLs.
Q44. TLS Stripping Attacks
Q44. What is a TLS-stripping attack, in one or two sentences? Name one HTTP response header that defends against it.
Answer: TLS stripping is a man-in-the-middle attack where the attacker downgrades the victim’s connection from HTTPS to plain HTTP, so traffic is sent unencrypted and can be read or modified. It often works on the first unprotected request before a redirect to HTTPS. The defending header is HSTS (Strict-Transport-Security), which forces the browser to use HTTPS for the site. Preloading HSTS protects even the very first visit.
Q45. CORS Security
Q45. Briefly explain what CORS does. Why is Access-Control-Allow-Origin: * a problem when combined with Access-Control-Allow-Credentials?
Answer: CORS (Cross-Origin Resource Sharing) lets a server selectively relax the Same-Origin Policy so chosen other origins can read its responses. Browsers actually forbid the combination of a wildcard * origin with Allow-Credentials: true, and for good reason: it would mean any website could make authenticated, cookie-bearing requests and read private user data. Even if a server tries to allow it by reflecting the origin instead of *, doing so for everyone is just as dangerous. Credentials should only be allowed for a small, explicitly trusted set of origins.
Q46. Path Traversal Vulnerabilities
Q46. Given res.sendFile(path.join(__dirname, 'uploads', req.query.name)): what vulnerability is this? Show a value of name that would exploit it.
Answer: This is a path traversal (directory traversal) vulnerability, because the user controls part of the file path. By using ../ sequences the attacker climbs out of the uploads folder. A value like ../../../../etc/passwd would read a sensitive system file. The fix is to normalize the resolved path and verify it still sits inside the uploads directory, and to allowlist or sanitize the filename.
Q47. Insecure Deserialization
Q47. Given data = pickle.loads(base64.b64decode(request.cookies['session'])): what is the security risk? Suggest one safer alternative to pickle.
Answer: The risk is insecure deserialization leading to remote code execution: pickle.loads on attacker-controlled data can run arbitrary Python during unpickling. Since the session cookie comes from the client, an attacker can craft a malicious pickle and take over the server. A safer alternative is to store session data as JSON (json.loads), which only produces plain data and cannot execute code. The cookie should also be signed/encrypted server-side so it cannot be tampered with.
Q48. SSRF Vulnerabilities
Q48. A site does requests.get(user_provided_url) to fetch a preview. What is this vulnerability called, and give one example URL an attacker might submit.
Answer: This is SSRF (Server-Side Request Forgery): the server is tricked into making requests to targets the attacker chooses. An attacker might submit an internal address like http://127.0.0.1:8080/admin or, in the cloud, the metadata endpoint http://169.254.169.254/latest/meta-data/ to steal credentials. This lets them reach internal services the firewall would normally block. Defenses include allowlisting destinations and blocking internal IP ranges and the metadata IP.
Q49. XXE Attacks
Q49. What is an XXE (XML External Entity) vulnerability? Briefly describe what it allows an attacker to do.
Answer: XXE happens when an XML parser processes external entities defined in attacker-supplied XML. By declaring an entity that points to a local file, the attacker can make the parser include that file’s contents in the response. For example, an entity referencing /etc/passwd can leak server files; it can also be used for SSRF or denial of service. The fix is to disable external entity and DTD processing in the XML parser.
Q50. Login Form Security
Q50. Name two security measures you would apply to a login form, and for each one briefly say what attack it helps prevent.
Answer: First, serve the form over HTTPS, which protects credentials from eavesdropping and man-in-the-middle interception. Second, add rate limiting plus account lockout, which slows down brute-force and password-spraying attacks. You could also add multi-factor authentication to defend against stolen passwords, and store passwords with bcrypt/Argon2 to limit damage from a database breach. Together these protect the login both in transit and against guessing.
