JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
165: Safe Handling of User Input
I want to show you a snippet of code that looks perfectly innocent. I've seen this exact pattern in dozens of junior developers' PRs, and it's one of the most dangerous mistakes you can make in a front-end application.
// A simple script to greet a user based on a URL parameter
const params = new URLSearchParams(window.location.search);
const name = params.get('name');
const welcomeDisplay = document.getElementById('welcome-message');
welcomeDisplay.innerHTML = `<strong>Welcome, ${name}!</strong>`;
At first glance, it works. If the URL is ?name=Alice, the page says Welcome, Alice!. But here is where things go south. What happens if a user (or a malicious actor) sends a link to someone else that looks like this?
?name=<img src=x onerror="alert('Your session cookie is: ' + document.cookie)">
Because we used .innerHTML, the browser doesn't just see a name; it sees an HTML img tag with a broken source. When that image fails to load, the onerror event fires, executing whatever JavaScript was inside it. This is a classic Cross-Site Scripting (XSS) attack. I've seen this used to steal session tokens, redirect users to phishing sites, or deface pages.
The Danger of Implicit Trust
The root of the problem is that .innerHTML tells the browser: "Take this string and parse it as actual HTML." When you mix that with data coming from a user—whether it's from a URL, a form input, or an API—you are essentially giving the user permission to write code directly into your page.
You might be tempted to try and "clean" the string using a regular expression to remove <script> tags. Don't do that. Attackers are incredibly creative; they can use different encodings, case-mixing (<sCrIpT>), or event handlers like onmouseover to bypass your filters. Trying to blacklist "bad" characters is a losing game.
Neutralizing Input with textContent
The simplest and most effective fix for the example above is to stop treating user input as HTML. If you just want to display text, use .textContent.
const params = new URLSearchParams(window.location.search);
const name = params.get('name');
const welcomeDisplay = document.getElementById('welcome-message');
// Create a strong element separately
const strongElement = document.createElement('strong');
strongElement.textContent = `Welcome, ${name}!`;
welcomeDisplay.innerHTML = ''; // Clear previous content
welcomeDisplay.appendChild(strongElement);
By using .textContent, the browser treats the input as literal text. If someone passes in <img src=x...>, the page will literally display those characters on the screen rather than executing them. It's a complete neutralization of the threat.
Handling Necessary HTML with Sanitization
Now, I know what you're thinking: "What if I actually need to allow some HTML?" Maybe you're building a blog editor that allows <b> and <i> tags. In those cases, you cannot use .textContent, and you absolutely cannot trust your own regex.
This is where you use a dedicated sanitization library. The industry standard is DOMPurify. It doesn't just look for "bad" words; it parses the HTML into a DOM tree, strips out everything that isn't on a strict "allow-list," and returns a clean string.
import DOMPurify from 'dompurify';
const rawUserInput = params.get('bio');
// User might have sent: <img src=x onerror=alert(1)> Hello!
const cleanHTML = DOMPurify.sanitize(rawUserInput);
welcomeDisplay.innerHTML = cleanHTML;
// Now it only contains the safe parts of the input.
My rule of thumb: Default to .textContent. If you absolutely must render HTML from a user, use a vetted library. Never try to write your own "safe" HTML parser.
📋 Practical Task
Secure the User Profile Bio Page
You are working on a profile page where users can set a "Bio." The current implementation is vulnerable to XSS. Your task is to fix the code so that the bio is displayed safely, but the "Last Updated" timestamp remains bolded.
Requirements:
- Ensure that any HTML tags provided in the
userBiovariable are rendered as literal text (not executed). - The
timestampmust still be rendered inside a<strong>tag. - Do not use
.innerHTMLto insert theuserBio.
<div id="profile-container"></div>
<script>
const userBio = "<img src=x onerror='alert(\"Hacked!\")'> I love JavaScript!";
const timestamp = "Updated 2 mins ago";
const container = document.getElementById('profile-container');
// FIX THIS PART:
// Currently: container.innerHTML = `<p>${userBio}</p> <strong>${timestamp}</strong>`;
// Your code here:
</script>There are no comments for now.