Vitalis

Store html into any database as string and prevent cross-site scripting AKA XSS

How to Safely Store HTML Content as string in any DB and Prevent XSS Attacks in YourDB.

Hello, folks! 👋 Recently, I embarked on setting up a simple blog infrastructure where I could create my own upload page, store data in a database, and then retrieve and display that data on a webpage. Pretty simple, right? Well, since I wanted to keep things lightweight, I opted for a NoSQL, document-oriented database (which basically stores data as key-value pairs in JSON-like documents).

big no-no

But here’s where things got tricky: I was storing the rich-text editor values as HTML strings. If you’re already thinking “Wait, that’s susceptible to XSS (cross-site scripting) attacks,” you’re absolutely right! Storing raw HTML as a string is abig no-nowhen it comes to security. So, what did I do to solve this? Let’s break it down!

πŸš€ TheSolution

My solution was to sanitize the HTML content from the rich-text editor. Essentially, I found a way to strip away harmful scripts and unwanted spaces, while allowing the necessary tags and attributes.

🧩 Step-by-Step Breakdown of theSolution

Step 1: What Does β€œSanitize” Mean,Anyway?

Great question! 😎 Do you remember the COVID-19 pandemic, when we had to sanitize our hands like a thousand times a day? The whole point was to kill or remove harmful bacteria that could make us sick. Well, sanitizing HTML is kinda the same thing. We’re going to:

  1. Remove harmful scripts(likescripttags that could execute malicious code).

  2. Allow tags and attributes that we want(for example, we’ll keep image tags and allowsrcandaltattributes).

  3. Trim spaces(because who needs extra spaces cluttering things up?).

And guess what? There’s a handy-dandy npm package calledsanitize-htmlthat does all of this for us! 🎉

Step 2: Installing thePackage

To installsanitize-html, just run this command in your project:

bash
npm install sanitize-html

Step 3: Writing theCode

Now, let’s dive into the code. Here’s a function that sanitizes the rich text content from the editor:

bash
import sanitizeHtml from 'sanitize-html';
export function sanitizeRichTextContent(content) {
  return sanitizeHtml(content, {
    allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']), allowedAttributes: {
      ...sanitizeHtml.defaults.allowedAttributes, img: ['src', 'alt'],
    }, exclusiveFilter: (frame) =>{ return frame.tag === 'script'; }, textFilter: (text) =>{ return text.replace(/\s+/g, " ").trim(); }
  });
}

πŸ“œ What Does This CodeDo?

  1. Tags: It allows all default HTML tags like<a>,<p>,<strong>, and<em>. Additionally, it allows the<img>tag, which is not included by default.

  2. Attributes: It ensures that allowed tags can only have specific attributes. For example,<img>tags can only havesrcandaltattributes—no shady JavaScript events likeonclickallowed! 🚫

  3. Text Cleaning: It removes any extra spaces and trims the content for a nice, clean output.

  4. No Scripts Allowed!: The function strictly removes any<script>tags to prevent potential cross-site scripting (XSS) attacks.

πŸ›  Step 4: Using the Sanitizer in YourBackend

Now that we have our sanitizer function, we need to use it before storing any data in our database. Here’s an example of how I did it in my code.

bash
// File: src/pages/api/upload-blog.js import clientPromise from '../../lib/mongodb';
import { createBlogPost, BlogPostSchema } from '../../models/BlogPost';
import { sanitizeRichTextContent } from '../../utils/htmlSanitizer';
export async function POST({ request }) {
  let blogData = await request.json();
  try {
    // Validate blogData against BlogPostSchema Object.keys(BlogPostSchema).forEach(key =>{
      if (BlogPostSchema[key].required&&!blogData[key]) {
        throw new Error(`${key} is required`);
      }
    });
    if (blogData) { blogData = sanitizeRichTextContent(blogData); } const client = await clientPromise;
    const db = client.db("blogDatabase");
    const collection = db.collection("posts");
    const blogPost = createBlogPost(blogData);
    const result = await collection.insertOne(blogPost);
    return new Response(JSON.stringify({ success: true, id: result.insertedId }), {
      status: 200, headers: { 'Content-Type': 'application/json', },
    });
  } catch (error) {
    return new Response(JSON.stringify({ success: false, error: error.message }), {
      status: 500, headers: { 'Content-Type': 'application/json', },
    });
  }
}

πŸ“ Key Takeaways

  • Sanitize your inputs!Whether you’re building a simple blog or a full-fledged app, never store raw HTML as a string in your database. Always clean it up first to avoid security risks like XSS attacks.

  • Use helpful tools!Thesanitize-htmlpackage makes it incredibly easy to filter out harmful content and ensure that only the tags and attributes you want are stored.

  • Store HTML as stringBy removing the spaces you have one continuous HTML which is taken as a long string.

That’s a wrap