DEV Community

Cover image for What Is Cross-Site Scripting (XSS)? Plain-English Guide
Bug Circuit
Bug Circuit

Posted on • Originally published at bugcircuit.com

What Is Cross-Site Scripting (XSS)? Plain-English Guide

Originally published on the Bug Circuit blog.

Cross-site scripting (XSS) is a security bug that lets an attacker sneak their own code into a page your visitors trust, so it runs inside their browser as if your site wrote it — quietly stealing login sessions, redirecting payments, or planting fake forms. It's one of the oldest and still most common web vulnerabilities, and it doesn't require breaking into your server — just finding one spot on your site that echoes back user input without cleaning it up first.

This is for owners of WordPress sites, Shopify stores, indie SaaS apps, or small business sites who've heard the term "XSS," maybe seen it flagged by a scanner, and want to know in plain English what it actually does and whether their contact or comment form is a target. By the end, you'll know how to test for it yourself and what a real fix looks like.

The short version: how XSS actually works

Every web page is a mix of your content and, sometimes, content your visitors typed in — a comment, a search term, a name field, a support ticket. Your site's job is to display that input back safely, as plain text.

XSS happens when your site instead treats that input as code and runs it. If someone types <script>steal_your_cookies()</script> into a form and your page prints it straight into the HTML without encoding it, the browser doesn't see text — it sees an instruction to execute. The script isn't hosted on some attacker's server pretending to be you; it runs directly inside your page, in the visitor's browser, with full access to whatever your page can see.

OWASP, the nonprofit that maintains the standard reference for web vulnerabilities, defines XSS as an injection attack where malicious scripts are injected into otherwise trusted, benign websites — and it's serious enough to be folded into the OWASP Top 10's Injection category, a list of the most critical risks to web applications.

The three flavors of XSS (and why the difference matters)

Not all XSS behaves the same way, and the type affects who's at risk and how urgently it needs fixing.

Type Where the malicious script lives How it usually gets delivered Real-world risk
Stored (persistent) XSS Saved in your database — a comment, review, forum post, profile bio Attacker submits it once through a form Every visitor who later views that page runs the script automatically — the most dangerous variant
Reflected XSS Bounced back in the page's response, never saved A crafted link with the script in the URL, sent via email or ad Only affects someone who clicks the malicious link
DOM-based XSS Injected entirely in the browser by client-side JavaScript Page JavaScript reads something like the URL fragment and writes it into the page without checking it Can happen even if your server-side code is perfectly clean

Stored XSS is the one to worry about most on a small site, because a single successful comment-form submission can quietly compromise every visitor who reads that page afterward — no clicking required.

What an attacker can actually do once their script runs

Once a script executes in a visitor's browser on your domain, it inherits whatever trust that visitor has placed in your site. Concretely, that means an attacker can:

  • Hijack a logged-in session. Read the session cookie or auth token and reuse it to log in as that user — including an admin — without ever knowing the password.
  • Inject a fake payment form. Overlay a convincing "update your card details" box on your real checkout or account page, capturing card numbers that go straight to the attacker instead of your payment processor.
  • Redirect or phish. Silently forward the visitor to a lookalike login page, or pop a fake "session expired, log in again" prompt that harvests credentials.
  • Log keystrokes or grab form data before it's even submitted.
  • Deface or spread. On stored XSS, the payload can rewrite the page for every subsequent visitor, or worm through comment sections automatically.

None of this requires the attacker to find a way into your hosting account or database directly — the browser does the work for them.

Is your contact or comment form actually exposed?

Any place your site takes text from a stranger and later displays it back — to that same visitor or to anyone else, including your own staff — is a candidate. Common spots on small sites:

  • Comment sections and product reviews
  • Contact forms that echo the name back on a "Thanks, {name}!" confirmation page
  • Search boxes that show "No results for {your search}"
  • Support/ticket forms an admin later opens in a dashboard
  • User profile fields (bio, display name, website URL)
  • Any WordPress plugin or theme that hasn't been updated in over a year

If your site is on WordPress, outdated plugins are the single most common source — vulnerable versions get published with fixes in the changelog, so an attacker just needs to know your plugin version. Not sure where you stand? Our free website security check flags exposed input points and out-of-date software without needing a login or card.

How to check it yourself (safely)

You can do a basic, non-destructive test on your own site — never test a site you don't own or have permission to test.

  1. Find a form field that gets displayed back somewhere (comment box, contact form, search).
  2. Submit a harmless test string: <script>alert('test')</script> or, if angle brackets get stripped, try "><img src=x onerror=alert(1)>.
  3. Load the page where that input is displayed back.
  4. If a popup box appears saying "test" or "1" — your input isn't being encoded, and that field is vulnerable.
  5. If the text shows up literally as <script>alert('test')</script> on the page (not as a popup), the field is encoding it correctly.

Delete your test comment afterward. This manual check only catches the obvious cases — automated scanners and a trained eye catch far more, including DOM-based XSS that never touches your server logs at all, which is one reason a manual review typically finds more than automated scanning alone.

How to actually fix it

The fix depends on where the bug lives, but the core principle from OWASP's Cross-Site Scripting Prevention Cheat Sheet is simple: never let untrusted input become code.

  • Encode output, don't just filter input. Escape special characters (<, >, ", ', &) whenever user data is written into HTML, so <script> renders as text, not code. Every modern templating framework (React, Vue, Twig, Blade) does this automatically by default — don't bypass it with functions like dangerouslySetInnerHTML or v-html unless the content is sanitized first.
  • Sanitize rich text properly. If you must allow some HTML (e.g., a WYSIWYG comment box), run it through a real sanitizer library like DOMPurify — don't hand-write a regex filter, which almost always misses an edge case.
  • Set a Content-Security-Policy header. This tells the browser which sources of script it's allowed to run, so even an injected <script> tag gets blocked. A reasonable starting policy: Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'. See MDN's CSP reference for the full syntax. You can check what headers your site currently sends with our security headers scanner.
  • Lock down cookies so a successful XSS can't be turned into a session hijack. Set Set-Cookie: sessionid=<value>; HttpOnly; Secure; SameSite=StrictHttpOnly stops JavaScript from reading the cookie at all, which blunts the most common XSS payoff even if a script does slip through.
  • Keep CMS/plugins current and remove unused ones. On WordPress, update core, theme, and every plugin promptly, and consider a web application firewall plugin (e.g., Wordfence or Sucuri) as a second layer, not a replacement for fixing the actual code.

None of these fixes require a rebuild — most are a template change, one HTTP header, and a cookie flag.

Key takeaways

  • XSS lets an attacker's script run inside your visitors' browsers on your own domain — it doesn't require hacking your server directly.
  • Stored XSS (comments, reviews, profile fields) is the highest-risk type because it hits every visitor automatically, not just people who click a bad link.
  • Watch any field that echoes user input back — contact forms, search boxes, comments — and test with a harmless <script>alert(1)</script> payload on your own site only.
  • Fix it with output encoding, a Content-Security-Policy header, and HttpOnly/Secure/SameSite cookie flags — not a hand-rolled filter.
  • Automated scanners miss a lot of real-world XSS, especially DOM-based cases; that's the gap a manual review is built to close.

If you'd rather have a person check for this instead of guessing from a checklist, that's exactly what Circuit is: a $49 one-time manual audit where a real security engineer tests your forms, comments, and inputs by hand and hands you a written report with exact fixes — not just a scanner printout. If you're not sure your site's been affected already, our guide on signs your website might be hackable is a good next read.

Top comments (0)