Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogCoursesPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Courses
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 25, 20264 min read

Preventing DOM-based XSS: A Guide for Modern JavaScript Apps

Learn how to stop DOM-based XSS by securing your client-side sinks and sources. Master practical input sanitization and secure coding techniques today.

JavaScriptSecurityWeb DevelopmentXSSFrontendDOM-based XSSSecure CodingWebBackend

During a recent security audit of a React-based dashboard, I watched a colleague trigger an alert box just by clicking a carefully crafted URL. It wasn't a server-side flaw; it was a classic case of DOM-based XSS. We were taking a parameter from the URL fragment and passing it directly into an .innerHTML call, effectively handing the keys to the browser's execution engine to any attacker who could trick a user into clicking a link.

It’s a subtle but dangerous vulnerability because the server never even sees the malicious payload. The entire attack happens in the victim's browser. If you’re building modern SPAs, understanding how data flows from sources to sinks is the most critical skill you can develop to ensure your client-side security is actually effective.

Identifying Sources and Sinks

To prevent DOM-based XSS, you have to think like a data-flow analyzer. You are looking for a path from a "source" (where untrusted data enters your app) to a "sink" (where that data is executed or rendered).

Common sources include:

  • location.search
  • location.hash
  • document.referrer
  • window.name

The sinks are the dangerous functions that can execute JavaScript or render HTML:

  • .innerHTML
  • .outerHTML
  • document.write()
  • eval()
  • setTimeout() (when passed a string instead of a function)

If you’re pulling data from a URL parameter and shoving it into element.innerHTML, you have a vulnerability. It’s that simple, and it’s that dangerous.

The Wrong Turn: Trusting "Safe" Input

We once tried to sanitize input using a basic regex that stripped out <script> tags. It felt clever at the time. However, an attacker can easily bypass this with <img src=x onerror=alert(1)> or other variations that don't rely on classic script tags.

We learned the hard way that blacklisting is a losing game. You will never catch every possible bypass. Instead, you need a robust strategy for secure coding that focuses on context-aware output encoding or, better yet, avoiding dangerous sinks entirely.

Strategies for Prevention

If you want to stop DOM-based XSS in its tracks, follow these three rules.

1. Prefer Safe APIs

Always use textContent or innerText instead of innerHTML. These properties treat the input as literal text, meaning the browser won't attempt to parse it as HTML.

JAVASCRIPT
// Dangerous
const userBio = new URLSearchParams(window.location.search).get(CE9178">'bio');
document.getElementById(CE9178">'bio-container').innerHTML = userBio;

// Secure
const userBio = new URLSearchParams(window.location.search).get(CE9178">'bio');
document.getElementById(CE9178">'bio-container').textContent = userBio;

2. Use a Sanitization Library

Sometimes you must render HTML from a user-provided string—maybe for a rich text editor. In that case, do not roll your own regex. Use a battle-tested library like DOMPurify. It’s the industry standard for stripping dangerous elements while keeping the safe ones.

JAVASCRIPT
import DOMPurify from CE9178">'dompurify';

const dirtyHtml = getFromUrl();
const cleanHtml = DOMPurify.sanitize(dirtyHtml);
document.getElementById(CE9178">'content').innerHTML = cleanHtml;

3. Implement a Content Security Policy (CSP)

A strong CSP is your last line of defense. By setting a Content-Security-Policy header, you can restrict where scripts can be loaded from and prevent the execution of inline scripts entirely. Even if you miss a vulnerability in your code, a strict CSP can prevent an attacker from successfully executing their payload.

Handling Data Responsibly

Beyond just the DOM, remember that security is holistic. If you're handling data that eventually hits your backend, make sure you aren't creating other vulnerabilities. For example, preventing mass assignment vulnerabilities with DTOs in Laravel and Express is just as important as sanitizing your client-side inputs.

If you’re working with legacy codebases or complex forms, you might also want to revisit XSS prevention strategies: A guide for modern web developers to ensure you're covered across all three XSS variants: Reflected, Stored, and DOM-based.

FAQ: Common Concerns

Q: Is innerHTML ever safe? A: Only if you are 100% sure the content is hardcoded in your source files or has been passed through a library like DOMPurify. If it touches user-controlled input, assume it’s unsafe.

Q: Does React/Vue/Angular protect me automatically? A: Mostly, yes. Frameworks like React sanitize data by default when you use curly braces {data}. However, if you explicitly use dangerouslySetInnerHTML in React or v-html in Vue, you are opting out of that protection and effectively creating a potential sink.

Q: What about eval()? A: Just don't use it. There is almost no scenario in modern application development where eval() is the right tool. It’s a massive security risk and a performance bottleneck.

Final Thoughts

Security isn't a feature you toggle on; it's a discipline. I'm still wary every time I see a developer reach for a dynamic sink. We've come a long way from the days of simple alert boxes, but the core issue—trusting data that comes from the user—remains the same. Keep your sinks narrow, your sources isolated, and always favor built-in browser protections over custom sanitization logic.

Next time, I'm planning to look into how we can automate these checks during the CI/CD pipeline, because manual code review is great, but catching these patterns before they hit production is even better.

Back to Blog

Similar Posts

Close-up of colorful programming code displayed on a computer screen, showcasing modern coding concepts.
SecurityJune 20, 20264 min read

XSS prevention strategies: A guide for modern web developers

XSS prevention strategies are essential for securing modern web apps. Learn to identify the three main variants and implement robust, layered defenses today.

Read more
SecurityJune 24, 2026
4 min read

Preventing BOLA Vulnerabilities in Multi-Tenant API Architectures

BOLA vulnerabilities can expose private data in multi-tenant apps. Learn how to secure your API endpoints by decoupling authorization from your business logic.

Read more
SecurityJune 24, 20264 min read

WebSocket security: How to prevent CSWSH in Node.js and Laravel

WebSocket security starts with preventing CSWSH. Learn how to validate origins and secure your real-time connections in Node.js and Laravel applications.

Read more