5 injection vulnerabilities hackers don't want developers to know about (and how to prevent them)

This page summarizes the projects mentioned and recommended in the original post on /r/node

Our great sponsors
  • Appwrite - The open-source backend cloud platform
  • InfluxDB - Collect and Analyze Billions of Data Points in Real Time
  • Onboard AI - Learn any GitHub repo in 59 seconds
  • DOMPurify

    DOMPurify - a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMPurify works with a secure default, but offers a lot of configurability and hooks. Demo:

    body, input.value property, or body are all different). If you need to insert untrusted input into raw HTML, use a well-tested sanitizer such as DOMPurify.

    Setting a strong Content Security Policy without unsafe-inline or unsafe-eval in the script-src or default-src directives is an effective defense-in-depth) measure to prevent modern browsers from executing attacker code even if the attacker is able to insert elements into the page.

    3. HTTP API injection

    RESTful APIs, GraphQL, and other HTTP-based APIs are ubiquitous. When a web application makes an API call to another service, injection vulnerabilities are possible when that request includes untrusted input.

    Consider a contrived example in which a web app integrates with a payments service that has a REST API endpoint for creating a subscription: POST /subscriptions/{product_id}?price_usd= where price_usd is optional, and a pre-configured price is used if omitted. If an attacker controls the value of product_id and passes a value of desired_product_id?price_id=0, the web app would end up making a request to POST /subscriptions/desired_product_id?price_id=0, which would allow the attacker to sign up for a free subscription.

    In JavaScript, the standard way to sanitize untrusted inputs in URL paths is encodeURIComponent, which replaces problematic characters such as ? and / with safe percent-encoded sequences. When inserting untrusted input into URL query parameters, new URLSearchParams(queryParams) provides a convenient, safe interface for building a query string from a JavaScript object of key-value pairs.

    4. Shell injection

    Backend APIs sometimes need to execute external commands on the machine where they run. Consider an API that performs WHOIS lookups for a requested domain by executing the whois command locally.

    Consider the following vulnerable Node.js code:

    const whois = child_process.execSync(`whois ${whoisRequest.domain}`);
    

    If an attacker can pass the domain reddit.com && rm -rf /, the backend will execute the command whois reddit.com && rm -rf /. The child_process.execSync function passes the command string to the shell (/bin/sh by default on Linux), which parses && rm -rf / as a subsequent command to wipe the filesystem.

    To avoid this issue, never pass untrusted input to a shell. Instead, use an interface such as child_process.execFileSync that executes a specific binary (which shouldn't be a shell!) and passes arguments as an array:

    const whois = child_process.execFileSync("whois", [whoisRequest.domain]);
    

    Now, even if the user passes a domain reddit.com && rm -rf /, that entire string will be passed as the command-line argument to whois, which will exit with an error but will not cause any harmful side-effects. Perhaps an even better solution would be to use a library to perform WHOIS queries without needing to execute a separate command.

    Astute readers may point out that validating the domain against a regex would also likely prevent shell injection in this case. However, avoiding the possibility of shell injection by using a safe interface that keeps untrusted input away from a shell's command parser is a more robust solution that avoids shell injection in all cases.

    5. Path traversal

    Finally, a path traversal vulnerability arises when an untrusted input is inserted into a filesystem path, which can cause the wrong file to be read or even written. Consider a backend API that reads a file at the path /teams/${team_id}/${report_name}.csv. If an attacker controls the value of report_name but not team_id, they could pass a report_name of ../other_team_id/private. This would cause the file /teams/team_id/../other_team_id/private.csv (resolved to /teams/other_team_id/private.csv) to be read, leaking data from a different team.

    To avoid path traversal vulnerabilities, never use untrusted input in file or directory names. It's safest always to control the names of files and directories, including IDs that you generate and control (e.g., UUIDs, KSUIDs, etc.). If the name of a file or directory absolutely must be derived from untrusted input, consider hashing it (e.g., using SHA-256) or at least encoding it into a format that doesn't include dots or slashes (e.g., URL-safe base64).

    Know of good Node.js libraries for avoiding injection vulnerabilities? Let folks know in the comments!

  • React

    The library for web and native user interfaces.

    The most robust protection against XSS is to use a client-side framework like React that lets you intuitively render untrusted inputs on a page without risking XSS. Be sure to avoid using dangerouslySetInnerHTML, which bypasses XSS protections.

  • Appwrite

    Appwrite - The open-source backend cloud platform. Add Auth, Databases, Functions, and Storage to your product and build any application at any scale while using your preferred coding languages and tools.

  • sanitizer-api

    The upcoming Sanitizer API - kinda like a native DOMPurify that provides el.setHTML() and Document.parseHTML()

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts