top of page

Preventing XSS Vulnerabilities in Modern Web Applications


Preventing XSS Vulnerabilities

If your internet searching brought you here and you already have a basic understanding of Cross-Site Scripting, welcome! Feel free to stick around and read the rest of this. However, you should be warned...this isn't aimed at you. What we want to do today is break down cross-site scripting for those who rely solely on AI to write their code.


It's a growing business, really, using AI platforms like Lovable and Base-44 to write apps. It's given people who don't know anything about programming the opportunity to make some remarkable things. Unfortunately, it's also given the same people the ability to unknowingly put themselves and their users at risk. And while the point of this post isn't to call attention to the dangers of vibe coding (though that's the point of this blog over here), we want to call attention to a specific danger that can be present in any web app: cross-site scripting.


Cross-Site Scripting (XSS), just so we're all on the same page, is essentially a way for bad actors to sneak malicious code into your web app. Once there, it can run in other users' browsers, doing...well, all kinds of damage. Maybe you've got a comments section on your site and someone posts a "comment" that's actually a script. If your app doesn't handle it correctly, that script executes when other users view the page, potentially stealing their data or messing with the site itself.


XSS ranks among the top web vulnerabilities, and it's damaging because it can lead to everything from minor annoyances to full-blown data breaches. This article is tailored for developers like you, the "vibe coders" who thrive on quick iterations, creative flows, and AI tools to crank out features fast. You're not bogged down by endless specs; you're building on intuition. But while speed and creativity are awesome, they can leave security gaps if you're not mindful.


Why This Matters

Amazon homepage with "Kilroy Was Here" graffiti. Ads feature spring style, gaming accessories, Echo Show 8. Offers on smartwatches, fitness gear.

What makes XSS particularly villainous is that it causes all kinds of mischief. It can grab login credentials (not good for, say, your banking website); it can allow for session hijacking, where attackers impersonate users (you don't want to explain to Aunt Ida that it wasn't you who sent her naughty images on Facebook); site defacement, turning your sleek design into graffiti (can you imagine going to Amazon and seeing a giant "Kilroy was here!" plastered all over it?). It's very easy to create a site or app that's vulnerable to XSS, and hackers love low-hanging fruit. Don't be the one who gives it to them.


Ignoring XSS could turn your fun side project into a liability overnight.




What Is XSS?

A Deeper Dive

As we said a moment ago, XSS is when corrupt input gets treated as executable code in the browser. A user submits something sketchy, your app echoes it back without proper handling, and the browser runs it as if it's part of your legit code. When I was learning about XSS myself, I practiced uploading code that returned the usernames and password hashes of every user on the site. And while the site was designed to be hacked (so no one was actually compromised), the vulnerability on the site was both very real, and very common.



Creamy yellow ice cream in a cardboard tub, close-up. The texture is smooth and inviting, set against a neutral background.

Basic Types of XSS

There are a few flavors. And while I would much rather ponder the varied flavors of Baskin Robbins, it's important to discuss the XSS flavors that are out there:


  • Stored XSS (or persistent) is when the malicious script gets saved in your database—like in a forum post—and then served to every user who views it. It's sneaky because one bad input affects many, many users.

  • Reflected XSS is more immediate: the input bounces right back in the response, the way your reflection bounces back from a mirror. For example, an attacker may alter an error message on a site to include a malicious script. Then, when you do an action that receives it, the malicious script runs on your browser.

  • DOM-Based XSS lives entirely on the client side. It's placed in your JavaScript code manipulating the Document Object Model (DOM), where user input alters the page structure in a vulnerable way (usually routing to a malicious link).


Computers operate online in a client-server relationship. The client is the one requesting access, the server is providing it. Let's say you are logging into Amazon. You are the client requesting connection to Amazon, and Amazon is the server, giving you access.

Why Browsers Execute It

Browsers are trusting by nature. They assume whatever your server sends is safe HTML, CSS, or JS. If it looks like code, it runs like code, no questions asked. Browsers can't tell a difference between data and code - all they do is follow directions. If you want your app to ignore or prevent something being entered, you have to tell it to ignore it. We'll cover examples of that in a few moments.



The Core Principle: Never Trust User Input

What Counts as “User Input”?

User input is broader than you might think. It's not just form fields, but also URLs, query parameters, cookies, HTTP headers, even data from APIs or third-party widgets. If you're pulling from internal tools or AI-generated content, treat that as untrusted too. Anything not hardcoded by you could be (or could have been) tampered with.


The bottom line is this: never trust input. ALL input must be validated and sanitized.


Input vs. Output

Here's a key distinction: input validation checks what comes in (e.g., is this a valid email?), while output encoding handles how it's displayed or used (e.g., turning special characters into harmless equivalents). So if you nail validation but forget encoding, you're still leaving doors wide open.


Let's look at how to sanitize input and handle encoding, so you can examine your code for issues and close those doors.


Sanitization and Validation of User Input

Input Validation

Stick to "allow lists"—only accept what you expect. For an email field, use a regex to ensure it matches the pattern. Numeric IDs? Enforce digits only. Limit lengths to prevent buffer overflows or denial-of-service attacks. This filters out junk early.


A buffer overflow attack—the short version, at least—is when too much information is added to the memory space, spilling over and letting an attacker take control, or causing things to break.


Make sure to properly escape or encode special characters like angle brackets, quotation marks, and ampersands. This prevents browsers and backend systems from treating them as executable code.


Sanitization

This is about neutralizing threats: strip or escape dangerous characters like <, >, or script tags. Use trusted libraries like DOMPurify for HTML, or OWASP Java Encoder. Never roll your own sanitizer from scratch—it's a recipe for bugs.


Limitations

Sanitization isn't foolproof. Clever attackers find ways around it, like encoding payloads or using obscure syntax. It's a layer, not the whole cake, but one layer of defense is aways better than none, so be sure to use your AI platform to check for this.



Use Frameworks and Libraries Correctly

A dimly lit library with shelves of books flanking a vintage arched stained glass window. Warm wooden tones and tranquil atmosphere.

Vibe coding apps are notorious for using either bad code libraries or, in some cases, completely fabricating libraries. Bad actors know this and will often take these fake libraries and craft real libraries containing malicious code. Then when your favorite coding app imports the library, it brings in the bad one.


Lean on maintained libraries: OWASP's for encoding, or framework-specific plugins. Skip npm packages with no recent updates—they're ticking time bombs, as any vulnerability in them that hasn't been fixed, won't be.


An npm package is a collection of reusable JavaScript code that has been shared by someone for others to easily incorporate into their projects.

Content Security Policy (CSP)

A CSP is a list of instructions a website gives to your browser that says: “Only trust these things. Block everything else.”


Those “things” can involve:

  • Where scripts (little programs) are allowed to come from,

  • Whether a page is allowed to run code that was typed directly into it,

  • Whether it’s allowed to load images, videos, or fonts from random places,


So instead of the browser trusting anything it sees, it follows a strict rulebook.


Why is it important?

Websites are often attacked by tricking them into running bad code that someone else sneaks in—usually through comments, forms, links, or (my favorite) steganography. Steganography is the practice of hiding code in a picture. So if your app allows picture uploads, but fails to check the pictures for hidden code, you could be allowing malicious scripts into your app.


Without a CSP, the browser might say, "Sure, I'll run whatever code shows up here." With a CSP, the browser says, "Nope. That code didn't come from a trusted place. I'm blocking it."


Why Developers Like CSPs (Even Non-Security Ones)

You don’t have to be a security expert to benefit from a CSP. It:


  • Adds a safety net when mistakes happen

  • Protects users even if a bug slips through by refusing to let it run

  • Forces cleaner, more predictable code

  • Makes attacks much noisier (i.e., easier to detect)


So the CSP, in a nutshell, adds a layer of protection to keep bad code from getting in, but also keeps bad code from running.



Third-Party and Supply Chain Risks

External Scripts and Widgets

Analytics trackers, ad networks, chat bots, and UI libs are usually created by third parties. And that makes sense...after all, you can't be expected to create and implement everything. The problem? They run with your site's full permissions, and one compromised vendor can put your entire app and organization at risk.


Mitigation Strategies

There are several strategies you can use to mitigate third-party risks. Let's look briefly at a few:

  • Subresource Integrity: a security feature that places a cryptographic hash in the <script> and <link> tags. Use Subresource Integrity (SRI) hashes to ensure scripts aren't tampered with.

  • Restrict CORS and origins: set up a server to accept API requests solely from trusted web domains, preventing unauthorized sites from accessing sensitive user information and allowing only specific, authorized origins.

  • Audit regularly: Regularly auditing software dependencies helps you find vulnerabilities, manage technical debt, and remove unused or obsolete libraries.



Testing and Review Before Publishing

Automated Testing

Run linters like ESLint with security plugins, static analyzers like SonarQube, or dep scanners like Snyk. Browser extensions like NoScript highlight issues.


Code Review Culture

Even for quick vibes, get a buddy to glance it over, someone who actually knows what and how to look for vulnerabilities.



Special Considerations for “Vibe Coding” and AI-Assisted Development

Common Risks

Vibe coding's speed is killer, but it means trusting gut over rigor. AI tools spit out code fast, but they might overlook security—like using innerHTML blindly (see above).


Practical Prompting Habits

When using AI, prompt for security: "Generate this but explain XSS risks." Review for red flags. Keep a personal checklist: validate inputs, encode outputs, no eval. This is actually one of the strongest practices you can adopt. By learning how to prompt for security, you can catch a great number of problems from the start. And if you're unsure how to do this, just ask.



Deployment and Ongoing Maintenance

Post-Launch Monitoring

Set up logs for suspicious activity, alerts via Sentry, or bug bounties on platforms like HackerOne. User feedback forms catch issues early.


Regular Updates

Patch everything—frameworks, libs. Watch CVEs on sites like NIST. Automate updates where (and if) possible.



Closing Thoughts

As a vibe coder, you're a builder at heart; embrace the reviewer role too. A few habits, like quick reviews and smart libraries, slash risks without killing your flow. Build secure, build fast, and keep the vibes alive.


And if you're still not sure, reach out! I'll gladly set up a time to review your code and let you know what, if any, vulnerabilities I find. I currently work with html, Python and JavaScript, but I will be adding more programming languages to that list in the future!

 
 
 

Comments


bottom of page