Open In App

How Can Injection Attacks Be Prevented in JSX?

Last Updated : 24 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we’ll learn how to defend React applications from injection attacks by blocking injection attacks within JSX code. We’ll look at essential ways for strengthening your code against potential weaknesses.

Security is still a top priority in the fast-paced world of online development. Injection attacks offer a substantial threat to React apps, which could compromise the integrity and user experience of your online project.

Let’s take an Example

Because of insufficient input validation, this online calculator is vulnerable to injection attacks. Users can take advantage of this flaw by inserting malicious code, such as JavaScript, into the input boxes. Because the calculator fails to sanitize and validate user input, attackers can execute arbitrary code, posing possible security issues such as Cross-Site Scripting (XSS) attacks.

Calculator.js Code example

Javascript




import React, { useState, useEffect } from "react";
import "./calc.css";
 
const Calculator = () => {
    const [expression, setExpression] = useState("");
 
    useEffect(() => {
        const handleKeyDown = (event) => {
            const { key } = event;
            event.preventDefault();
            handleKeyPress(key);
        };
 
        window.addEventListener("keydown", handleKeyDown);
 
        return () => {
            window.removeEventListener("keydown", handleKeyDown);
        };
    }, [expression]);
 
    const handleKeyPress = (key) => {
        if (key.length === 1) {
            handleAppend(key);
        } else {
            switch (key) {
                case "Enter":
                case "=":
                    handleEvaluate();
                    break;
                case "Backspace":
                case "Delete":
                    handleBackspace();
                    break;
                default:
                    break;
            }
        }
    };
 
    const handleAppend = (value) => {
        setExpression((prevExpression) => prevExpression + value);
    };
 
    const handleEvaluate = () => {
        try {
            const result = eval("(" + expression + ")");
            setExpression(result.toString());
        } catch (error) {
            console.error(error);
            setExpression("Error");
        }
    };
 
    const handleClear = () => {
        setExpression("");
    };
 
    const handleBackspace = () => {
        setExpression(expression.slice(0, -1));
    };
 
    return (
        <div className="calculator">
            <h2>Online Calculator</h2>
            <div
                className="display"
                tabIndex="0"
                onKeyDown={(e) => e.preventDefault()}
            >
                {expression}
            </div>
            <div className="buttons">
                <button className="operator" onClick={() => handleAppend("+")}>
                    +
                </button>
                <button className="operator" onClick={() => handleAppend("-")}>
                    -
                </button>
                <button className="operator" onClick={() => handleAppend("*")}>
                    *
                </button>
                <button className="operator" onClick={() => handleAppend("/")}>
                    /
                </button>
                <button className="operator" onClick={handleClear}>
                    C
                </button>
                <button className="equal" onClick={handleEvaluate}>
                    =
                </button>
 
                <button className="number" onClick={() => handleAppend("7")}>
                    7
                </button>
                <button className="number" onClick={() => handleAppend("8")}>
                    8
                </button>
                <button className="number" onClick={() => handleAppend("9")}>
                    9
                </button>
                <button className="operator" onClick={() => handleAppend(".")}>
                    .
                </button>
 
                <button className="number" onClick={() => handleAppend("4")}>
                    4
                </button>
                <button className="number" onClick={() => handleAppend("5")}>
                    5
                </button>
                <button className="number" onClick={() => handleAppend("6")}>
                    6
                </button>
                <button className="del" onClick={handleBackspace}>
                    DEL
                </button>
 
                <button className="number" onClick={() => handleAppend("1")}>
                    1
                </button>
                <button className="number" onClick={() => handleAppend("2")}>
                    2
                </button>
                <button className="number" onClick={() => handleAppend("3")}>
                    3
                </button>
                <button className="equal" onClick={handleEvaluate}>
                    =
                </button>
            </div>
        </div>
    );
};
 
export default Calculator;


Output:

Example-Output

Online vulnerable calculator

The provided Calculator.js code is vulnerable to cross-site scripting (XSS) attacks due to the direct evaluation of user input in the `handleEvaluate` function. This vulnerability allows a malicious user to inject arbitrary JavaScript code into the application, which can then be executed when the user clicks the “=” button.

The `eval()` method can run arbitrary code, which can lead to security vulnerabilities if user input is not properly sanitized or checked.

Because of insufficient input validation, this online calculator is vulnerable to injection attacks. Users can take advantage of this flaw by inserting malicious code, such as JavaScript, into the input boxes.

To maintain web application security, such direct assessment of user input must be avoided, and safer solutions for processing and parsing user-generated material must be used.

How JSX prevents injection attacks

Approach 1: Using DOMPurify

DOMPurify is a quick and tolerant DOM-only XSS (cross-site scripting) sanitizer for HTML, MathML, and SVG. It is written in JavaScript and is compatible with all modern browsers. DOMPurify sanitizes HTML and prevents XSS attacks by removing any harmful HTML, hence preventing XSS attacks and other security issues.
Install the library using npm

npm install dompurify

Import the required modules in your code

import dompurify from 'dompurify';

Syntax

import dompurify from 'dompurify';
const App = () => {
const dirtyHtmlString = '<div><a href="javascript:alert("XSS");>Click me</a></div>';
const sanitizedHtmlString = dompurify.sanitize(dirtyHtmlString, {FORCE_BODY: true});
return (
<div dangerouslySetInnerHTML={{ __html: sanitizedHtmlString }} />
);
};
export default App;

Here’s a complete example

Javascript




import React, { useState } from "react";
import DOMPurify from "dompurify";
 
const UserInputComponent = ({ userInput }) => {
  const escapedContent = <div>{userInput}</div>;
  const purifiedContent = (
    <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
  );
 
  return (
    <div>
      <h2>Automatic Escaping</h2>
      {escapedContent}
 
      <h2>Sanitization with DOMPurify</h2>
      {purifiedContent}
    </div>
  );
};
 
export default function App() {
  const [userInput, setUserInput] = useState(
    "<img src=x onerror=alert('XSS attack') />"
  );
 
  return (
    <div>
      <h1>React Automatic Escaping and Sanitization Example</h1>
      <label>
        User Input:
        <input
          type="text"
          value={userInput}
          onChange={(e) => setUserInput(e.target.value)}
        />
      </label>
      <UserInputComponent userInput={userInput} />
    </div>
  );
}


Output:

Dompurify

DOMPurify

Approach 2: Declarative programming

Declarative programming, as demonstrated by React, relieves developers of direct DOM manipulation by offering a higher-level abstraction. Developers express the intended UI structure for a specific state rather than explicitly specifying how to update the DOM based on changes in the application state. The virtual DOM and validation process in React then handles the efficient update of the actual DOM automatically.

Syntax

// Functional Updates with setItems:
setItems((prevItems) => [...prevItems, newItem]);

The functional form of `setItems` to update the `items` state based on the previous state. This is a declarative way of updating state, ensuring that you are working with the latest state.

This approach enhances code readability and maintainability while also reducing the likelihood of frequent problems associated with manual DOM manipulation in large systems. Developers can concentrate on describing how the UI should look, while the framework handles the implementation details.

Here’s a complete example

Javascript




import React, { useState } from "react";
 
const InteractiveList = () => {
    const [items, setItems] = useState([]);
    const [newItem, setNewItem] = useState("");
 
    const addItem = () => {
        if (newItem.trim() !== "") {
            setItems((prevItems) => [...prevItems, newItem]);
            setNewItem("");
        }
    };
 
    const removeItem = (index) => {
        setItems((prevItems) => prevItems.filter((_, i) => i !== index));
    };
 
    return (
        <center>
            <div style={{ marginTop: "8rem" }}>
                <h1>Add New Items</h1>
                <input
                    type="text"
                    placeholder="Enter item"
                    value={newItem}
                    onChange={(e) => setNewItem(e.target.value)}
                />
                <button onClick={addItem}>Add Item</button>
 
                <ul>
                    {items.map((item, index) => (
                        <li key={index}>
                            {item}
                            <button
                                onClick={() => removeItem(index)}
                                style={{ margin: "0.5rem" }}
                            >
                                Remove
                            </button>
                        </li>
                    ))}
                </ul>
            </div>
        </center>
    );
};
 
export default InteractiveList;


Output:

Declarative-Appc

Declarative programming

In the end, by cleansing HTML, DOMPurify provides a strong barrier against XSS attacks. Declarative programming, as demonstrated by React, improves code readability and maintenance while minimizing the risks associated with manual DOM manipulation. Combining these approaches increases web application security and development efficiency, delivering a clean environment while simplifying coding.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads