Untrusted input arrives
A user profile, message, search term, URL value, cookie, or header reaches the application.
visitor@learn:~$ cat introduction-to-xss.html
Cross-site scripting happens when untrusted text is placed into a web page in a way that the browser treats as HTML or JavaScript instructions instead of plain user data. The attacker is usually targeting users of a trusted site, not only the server itself.
https://site.example/search?q=<script>alert('XSS')</script>
is a reflected XSS-style URL when the page prints q back into the response without encoding it.
A user profile, message, search term, URL value, cookie, or header reaches the application.
The application places that value into HTML, JavaScript, attributes, or the DOM.
If the context is unsafe, the browser interprets attacker-controlled text as code.
response = "<p>Your search for " + searchTerm + " returned 8 records</p>"
The attacker submits text that contains HTML or JavaScript into a vulnerable field.
The victim loads a page, link, or feature they believe belongs to the legitimate domain.
The malicious value becomes part of the page that the browser receives or constructs.
The browser gives the script access allowed to that origin, such as the page DOM and some cookies.
HttpOnly, SameSite, and secure session design can reduce impact, but they do not make XSS harmless.
The poisoned delivery
The application reflects attacker-supplied input back in the immediate response, often through a URL parameter.
/search?q=<script>...</script>
The landmine
The malicious value is saved by the application, then later served to victims from a database or content store.
comment = "<script>...</script>"
The inside job
Client-side JavaScript reads untrusted browser data and writes it into an unsafe DOM sink.
location.hash -> innerHTML
A description field inserted directly into an image attribute can escape the attribute and add new markup.
<img src="file.jpg" alt="${user_description}" />
Fragments after # are not sent to the server, but page JavaScript can still read and mishandle them.
https://trusted-bank.example/help#<img src=x onerror="...">
A common reflected XSS pattern appears when a search page reads a URL parameter and prints it back into the HTML response. The URL looks like it belongs to the trusted site, but the query string carries attacker-controlled text.
https://site.example/search?q=eggs
<p>Your search for eggs returned 8 records</p>
The search term is displayed as normal page text.
https://site.example/search?q=<script>alert('XSS')</script>
<p>Your search for <script>alert('XSS')</script> returned 8 records</p>
If the app inserts the value as raw HTML, the browser may execute it instead of displaying it.
<script>alert('XSS')</script>.
Encoding preserves the value as text for the current output context.
Scripts may try to read accessible cookies or session data and send them to an attacker-controlled endpoint.
XSS code can rewrite the DOM, change visible page content, or damage trust in the site.
A fake login, payment, or re-authentication prompt can appear inside a legitimate domain.
Any input that becomes output should be treated as untrusted. The risky moment is when data crosses into HTML, attributes, JavaScript, CSS, URLs, or DOM APIs.
Forms, search bars, HTTP parameters, hidden fields, cookies, headers, profile values, filenames, and comments.
Input displayed as-is, partially stripped characters, broken layout, unexpected alerts, or HTML that appears to become markup.
Do not trust client-side checks. Validate on the server, and use client validation only for quick feedback.
Allow only necessary characters, enforce length limits, reject invalid formats, and handle dangerous requests deliberately.
const usernameWhitelist = /^[A-Za-z0-9_]+$/;
if (!usernameWhitelist.test(username)) {
return res.status(400).send("Invalid username");
}
Encode user data for the exact context where it will be rendered. HTML body, attributes, URLs, CSS, and JavaScript need different treatment.
<script>alert(1);</script> <script>alert(1);</script>
Removing only <script> is bypassable. Attackers can use mixed casing, malformed tags, event handlers, encodings, or different contexts.
Prefer textContent over innerHTML. In React, normal JSX text rendering escapes values by default.
Usually keep values in their original form and encode when outputting. The correct encoding depends on future use.