68

Assume I have a page with an input box. The user types something into the input box and hits a button. The button triggers a function that picks up the value typed into the text box and outputs it onto the page beneath the text box for whatever reason.

Now this has been disturbingly difficult to find a definitive answer on or I wouldn't be asking but how would you go about outputting this string:

<script>alert("hello")</script> <h1> Hello World </h1>

So that neither the script is executed nor the HTML element is displayed?

What I'm really asking here is if there is a standard method of avoiding both HTML and Script injection in Javascript. Everyone seems to have a different way of doing it (I'm using jQuery so I know I can simply output the string to the text element rather than the html element for instance, that's not the point though).

7
  • 1
    possible duplicate of How to prevent Javascript injection attacks within user-generated HTML Commented Dec 31, 2013 at 10:04
  • 3
    Do you want to block all HTML injection, or just unsafe ones? Commented Dec 31, 2013 at 10:05
  • 1
    Also, if the use case is really what you say and this is client-side JavaScript only, you really don't need to prevent "injection". The user can only attack himself if the input isn't shown to anyone else (and if it's shown to other users you'd sanitize the input server-side). Commented Dec 31, 2013 at 10:07
  • 1
    All, this is more about an explanation of the concept and methods to prevent these kind of things happening. And what constitutes an unsafe html injection as opposed to a safe one? Commented Dec 31, 2013 at 10:09
  • <h1>Hello World</h1> is a safe injection because it doesn't present a security risk to the user. If you want to prevent HTML/JS injection, you either remove on encode HTML tags. It's simple as that. Commented Dec 31, 2013 at 10:12

8 Answers 8

96

You can encode the < and > to their HTML equivelant.

html = html.replace(/</g, "&lt;").replace(/>/g, "&gt;");

How to display HTML tags as plain text

Sign up to request clarification or add additional context in comments.

8 Comments

It's a very simple regular expression - the expression is basically <. in javascript a regular expression is defined inside slashes (//) and the g means it should search for all occurrences (g = global).
@dlampard Note that this is exactly what .text() does, so if you have jQuery there's no need for a custom regex.
Okay, I'm trying to avoid using jQuery because it obscures what is actually going on while im trying to understand the concepts and basics. I do love it though.
Any idea on what to do if you'd like to allow only some tags, such as bold or italics?
This will only prevent <script> injections, however that doesn't mean that someone couldn't use a template string to inject a script. Keep this in mind folks who look for this as a quick solution to solve everything. example: ${window.location='google.com'}
|
13
myDiv.textContent = arbitraryHtmlString 

as @Dan pointed out, do not use innerHTML, even in nodes you don't append to the document because deffered callbacks and scripts are always executed. You can check this https://gomakethings.com/preventing-cross-site-scripting-attacks-when-using-innerhtml-in-vanilla-javascript/ for more info.

8 Comments

note that the ask didn't include a jquery tag, so you should try and answer in plain javascript.
I'll elaborate and provide a jquery-less answer if the OP asks for it. The principle would be the same here.
I'm not entirely sure why but if the value of html is 0 this seems to return an empty string.
You must pass it a string, so "0" would work. you can check the source github.com/jquery/jquery/blob/2.2-stable/src/core/parseHTML.js and see it discards non string inputs
Question: Are you sure that temp.innerHTML = arbitraryHtmlString; won't start pre-loading images and running any onload handlers defined in image tags and the like?
|
2

A one-liner:

var encodedMsg = $('<div />').text(message).html();

See it work:

https://jsfiddle.net/TimothyKanski/wnt8o12j/

Comments

0

I use this function htmlentities($string):

$msg = "<script>alert("hello")</script> <h1> Hello World </h1>"
$msg = htmlentities($msg);
echo $msg;

1 Comment

I think he meant client side. Your simple answer is on the backend side
0

From here

var string="<script>...</script>";
string=encodeURIComponent(string); // %3Cscript%3E...%3C/script%3

Comments

0

My solution using typescript + decorators + regex

const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
return value.replace(removeTag, "");

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function filter(target) {
    return class extends target {
        constructor(...args) {
            super(...args);
        }
        setState(opts) {
            const state = {
                username: this.filter(opts.username),
                password: this.filter(opts.password),
            };
            super.setState(state);
        }
        filter(value) {
            const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
            return value.replace(removeTag, "");
        }
    };
}
let Form = class Form {
    constructor() {
        this.state = {
            username: "",
            password: "",
        };
    }
    setState(opts) {
        this.state = {
            ...this.state,
            ...opts,
        };
    }
    getState() {
        return this.state;
    }
};
Form = __decorate([
    filter,
    __metadata("design:paramtypes", [])
], Form);
function getElement(key) {
    return document.getElementById(key);
}
const button = getElement("btn");
const username = getElement("username");
const password = getElement("password");
const usernameOutput = getElement("username-output");
const passwordOutput = getElement("password-output");
function handleClick() {
    const form = new Form();
    form.setState({ username: username.value, password: password.value });
    usernameOutput.innerHTML = `Username: ${form.getState().username}`;
    passwordOutput.innerHTML = `Password: ${form.getState().password}`;
}
button.onclick = handleClick;
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      :root {
        --bg: #1d1907;
        --foreground: #e3e0cd;
        --primary: #cfb53b;
        --black: #333;
        --white: #fafafa;
      }

      @keyframes borderColor {
        from {
          border-bottom: 1px solid var(--foreground);
        }

        to {
          border-bottom: 1px solid var(--primary);
        }
      }

      * {
        outline: none;
        border: none;
      }

      body {
        padding: 0.5rem;
        font-family: "Fira Code";
        background-color: var(--bg);
        color: var(--foreground);
      }

      input {
        border-bottom: 1px solid var(--foreground);
        background-color: var(--black);
        color: var(--foreground);
        padding: 0.5rem;
      }

      input:focus {
        animation-name: borderColor;
        animation-duration: 3s;
        animation-fill-mode: forwards;
      }

      button {
        padding: 0.5rem;
        border-radius: 3px;
        border: 1px solid var(--primary);
        background-color: var(--primary);
        color: var(--white);
      }

      button:hover,
      button:active {
        background-color: var(--white);
        color: var(--primary);
      }

      .form {
        margin-bottom: 2rem;
      }
    </style>
    <title>Decorator</title>
  </head>
  <body>
    <h1>Prevent Injection</h1>
    <div class="form">
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" id="username" placeholder="Type your username" />
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" id="password" placeholder="Type your password" />
      </div>
      <div class="form-group">
        <button id="btn">Enviar</button>
      </div>
    </div>
    <div class="form-result">
      <p id="username-output">Username:</p>
      <p id="password-output">Password:</p>
    </div>
    <script src="/dist/pratica1.js"></script>
  </body>
</html>

Typescript Code bellow:

    type State = {
  username: string;
  password: string;
};

function filter<T extends new (...args: any[]) => any>(target: T): T {
  return class extends target {
    constructor(...args: any[]) {
      super(...args);
    }

    setState(opts: State) {
      const state = {
        username: this.filter(opts.username),
        password: this.filter(opts.password),
      };
      super.setState(state);
    }

    filter(value: string) {
      const removeTag = new RegExp("(<[a-zA-Z0-9]+>)|(</[a-zA-Z0-9]+>)", "g");
      return value.replace(removeTag, "");
    }
  };
}

@filter
class Form {
  private state: State;

  constructor() {
    this.state = {
      username: "",
      password: "",
    };
  }

  setState(opts: State) {
    this.state = {
      ...this.state,
      ...opts,
    };
  }

  getState() {
    return this.state;
  }
}

function getElement(key: string): HTMLElement | null {
  return document.getElementById(key);
}

const button = getElement("btn") as HTMLButtonElement;
const username = getElement("username") as HTMLInputElement;
const password = getElement("password") as HTMLInputElement;
const usernameOutput = getElement("username-output") as HTMLParagraphElement;
const passwordOutput = getElement("password-output") as HTMLParagraphElement;

function handleClick() {
  const form = new Form();
  form.setState({ username: username.value, password: password.value });
  usernameOutput.innerHTML = `Username: ${form.getState().username}`;
  passwordOutput.innerHTML = `Password: ${form.getState().password}`;
}

button.onclick = handleClick;

Comments

-1

Use this,

function restrict(elem){
  var tf = _(elem);
  var rx = new RegExp;
  if(elem == "email"){
       rx = /[ '"]/gi;
  }else if(elem == "search" || elem == "comment"){
    rx = /[^a-z 0-9.,?]/gi;
  }else{
      rx =  /[^a-z0-9]/gi;
  }
  tf.value = tf.value.replace(rx , "" );
}

On the backend, for java , Try using StringUtils class or a custom script.

public static String HTMLEncode(String aTagFragment) {
        final StringBuffer result = new StringBuffer();
        final StringCharacterIterator iterator = new
                StringCharacterIterator(aTagFragment);
        char character = iterator.current();
        while (character != StringCharacterIterator.DONE )
        {
            if (character == '<')
                result.append("&lt;");
            else if (character == '>')
                result.append("&gt;");
            else if (character == '\"')
                result.append("&quot;");
            else if (character == '\'')
                result.append("&#039;");
            else if (character == '\\')
                result.append("&#092;");
            else if (character == '&')
                result.append("&amp;");
            else {
            //the char is not a special one
            //add it to the result as is
                result.append(character);
            }
            character = iterator.next();
        }
        return result.toString();
    }

Comments

-1

Try this method to convert a 'string that could potentially contain HTML code' to 'text format':

$msg = "<div></div>";
$safe_msg = htmlspecialchars($msg, ENT_QUOTES);
echo $safe_msg;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.