skip to Main Content

I want to show status text depending on response received on axios post. My code is as below.

import React from 'react';
import axios from 'axios';

const handleSubmit = (email, pass) => {

    const data = {
        'user': email.value,
        'password': pass.value,
    };

    const headers = {
        "Content-Type": "application/json",
    }

    axios.post('/auth', data, {
        headers: headers
    })
    .then( function (res) {
        //do auth success stuff
    })
    .catch(function (error) {
        //auth failed, show status to user
        var err = document.getElementById("error");
        err.innerHTML = "Authentication failed";
        err.hidden = false;
    });
}

const Login = () => {
    return(
        <form
            onSubmit={(event) => {
                event.preventDefault()
                const [email, password] = event.target.elements;
                handleSubmit(email, password);
            }}>       
        
            <div className="medium-text">
                <div className="center">
                    <input type="text" id="email" name="email" placeholder="Email" />
                    <input type="password" id="password" name="password" placeholder="Password"/>
                    <div className="">
                        <button type="submit">Submit</button><br/><br/>
                    </div>
                    <div id="error" hidden>
                    </div>
                </div>
            </div>
        </form>
    );
};

export default Login;

Now as you can see when authentication fails, I have below code.

    //auth failed, show status to user
    var err = document.getElementById("error");
    err.innerHTML = "Authentication failed";
    err.hidden = false;

So my question is, is it safe to write code this way, as I read somewhere in react we have something like dangerouslySetInnerHTML and there is a chance somebody may hack if we use innerHtml directly, how is that possible.

2

Answers


  1. Yes it’s potentially a vector for cross-site scripting. Also, it’s simply not the React way.

    Instead, store the error in state and render it if required. There’s no need to use DOM methods at all

    const authenticate = async (user, password) => {
      try {
        // Axios defaults to sending JSON
        const res = await axios.post("/auth", { user, password });
    
        // do auth success stuff...
      } catch (error) {
        // you want to know why things failed, right?
        console.error(
          "/auth request failed",
          error.response?.data,
          error.toJSON?.() ?? error,
        );
        throw new Error("Authentication failed");
      }
    };
    

    Here I’m using state to hold not only any error but also the input values. This method is referred to as controlled components.

    import { useState } from "react";
    
    const Login = () => {
      // state for controlled inputs
      const [user, setUser] = useState("");
      const [password, setPassword] = useState("");
    
      // state for auth errors
      const [error, setError] = useState(null);
    
      const handleSubmit = async (e) => {
        e.preventDefault();
        try {
          await authenticate(user, password);
        } catch (err) {
          setError(err.message);
        }
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <div className="medium-text">
            <div className="center">
              <input
                type="text"
                id="email"
                name="email"
                placeholder="Email"
                value={user}
                onChange={(e) => setUser(e.target.value)}
              />
              <input
                type="password"
                id="password"
                name="password"
                placeholder="Password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
              />
              <div className="">
                <button type="submit">Submit</button>
              </div>
    
              {/* conditionally render any error */}
              {error && <div id="error">{error}</div>}
            </div>
          </div>
        </form>
      );
    };
    
    Login or Signup to reply.
  2. Directly manipulating the innerHTML property, as you’re doing in your code, is not a recommended practice in React because it can expose your application to potential security vulnerabilities such as cross-site scripting (XSS) attacks.

    Instead of setting innerHTML, it’s safer to use React state to manage the visibility and content of your error message.

    You can write your code like this to use React state:-

    import React, { useState } from 'react';
    import axios from 'axios';
    
    const Login = () => {
      const [error, setError] = useState(null);
    
      const handleSubmit = (email, pass) => {
        const data = {
          user: email.value,
          password: pass.value,
        };
    
        const headers = {
          'Content-Type': 'application/json',
        };
    
        axios
          .post('/auth', data, {
            headers: headers,
          })
          .then(function (res) {
            // do auth success stuff
          })
          .catch(function (error) {
            // auth failed, show status to user
            setError('Authentication failed');
          });
      };
    
      return (
        <form
          onSubmit={(event) => {
            event.preventDefault();
            const [email, password] = event.target.elements;
            handleSubmit(email, password);
          }}
        >
          <div className="medium-text">
            <div className="center">
              <input type="text" id="email" name="email" placeholder="Email" />
              <input type="password" id="password" name="password" placeholder="Password" />
              <div className="">
                <button type="submit">Submit</button>
                <br />
                <br />
              </div>
              {error && <div id="error">{error}</div>}
            </div>
          </div>
        </form>
      );
    };
    
    export default Login;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search