Sling Academy
Home/Node.js/Express + Handlebars: Escaping Sensitive HTML Output & JavaScript

Express + Handlebars: Escaping Sensitive HTML Output & JavaScript

Last updated: January 18, 2024

Introduction

When building web applications with Express and Handlebars, one important consideration is ensuring that your application is secure against Cross-Site Scripting (XSS) attacks. XSS attacks occur when an attacker manages to inject malicious scripts into web pages viewed by other users. To guard against this, it’s crucial to escape sensitive HTML output and JavaScript in your Express app using Handlebars.

In this tutorial, we’ll explore best practices for securing your Express application by utilizing Handlebars’ built-in escaping functions to prevent XSS attacks.

Understanding HTML Escaping

Escaping HTML is the process of converting special characters (like <, >, and &) into their corresponding HTML entities (like &lt;, &gt;, and &amp;). This prevents these characters from being interpreted as HTML code.

Handlebars, by default, escapes all output using the {{ }} syntax. When variables are displayed using {{ }}, Handlebars automatically escapes the content. For example:

{{ userContent }}

This ensures that if userContent contains any HTML, it will be displayed as a string rather than raw HTML.

Using Handlebars’ SafeString

Sometimes, you may want to allow certain HTML tags for formatting purposes. In such cases, Handlebars provides the SafeString function to render HTML without escaping:

Handlebars.registerHelper('safe', (text) => {
  return new Handlebars.SafeString(text);
});

This helper can be used in your templates like so:

{{{ safe userContent }}}

Caution: Only use SafeString when you are certain that the content to be rendered is clean or has been sanitized to avoid XSS attacks.

Sanitizing Input

Sanitization is the process of cleaning user input so that it’s safe to render or store. While Handlebars helps prevent XSS by escaping HTML, you can use sanitizing libraries such as DOMPurify or xss to strip out potential XSS payloads before they’re rendered:

const DOMPurify = require('dompurify')(window);
const clean = DOMPurify.sanitize(userContent);
// Handlebars' template
{{ clean }}

Note: Remember to run sanitization on the server-side before rendering the content.

Content-Security-Policy

Setting up a Content-Security-Policy (CSP) header can provide an additional layer of security. CSP is an added security layer that helps to detect and mitigate certain types of attacks, including XSS and data injection attacks.

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    imgSrc: ['images.com'],
    styleSrc: ["'self'", 'maxcdn.bootstrapcdn.com'],
    scriptSrc: ["'self'", "'unsafe-inline'"],
    frameSrc: ['none'],
  }
}));

In the above code, helmet is a middleware for Express that can set the appropriate HTTP headers. Be careful when allowing 'unsafe-inline' in scriptSrc as it can make your CSP less effective.

Securing JavaScript Outputs

When outputting JavaScript variables in your Handlebars templates, make sure you also escape these correctly to prevent XSS:

<script>
  var userInfo = JSON.parse("{{ escapeJSON userInfo }}");
</script>

In this code snippet, we assume escapeJSON is a helper that escapes the JSON representation of a user’s information.

Conclusion

In conclusion, preventing XSS attacks in Express applications using Handlebars requires a combination of default escaping, use of helpers like SafeString when appropriate, input sanitization, strong Content-Security-Policies, and proper JavaScript escaping. Putting these best practices into action will help to maintain a robust defense against potential security threats in web development.

Always stay updated with the latest security guidelines and Handlebars documentation as new potential threats and countermeasures may evolve over time.

Next Article: Express + Handlebars + TypeScript: Tutorial & Examples

Previous Article: Express + Handlebars: How to Include Subviews

Series: Node.js & Express Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates