XSS Security with string templates?

Hi,

I am struggling about what is the best practice to avoid XSS attacks on a dynamic javascript page which uses template strings to be created.

Lets have an easy example (which means don’t think about if that really makes sense in the real life :slight_smile: :

I have a page containing the users profile data like

Name:
Street:
City:

lets say I create this page with a template string like

const userTemplate = (user) =>
{
    return `<div class = "user">
                   <div class = "userName">${user.name}</div>
                   <div class = "userStreet">${user.street}</div>
                   <div class = "userCity">${user.city}</div>
               </div>`;
}

and add it to the actual page with something like

document.getElementbyId("content").innerHTML = userTemplate(user);

So far so good. Nice looking modern code.

No let’s say I add an edit button next to the user and if you click on this button a new popup open where the user can edit his data. This popup is also created by a template like:

const editUserTemplate = (user) =>
{
    return `<div class = "editUser">
                   <div class = "userName"><input id = "name" class = "input" type = "text" value = "${user.name}"></div>
                   <div class = "userStreet"><input id = "street" class = "input" type = "text" value = "${user.street}"></div>
                   <div class = "userCity"><input id = "city" class = "input" type = "text" value = "${user.city}"></div>
                   <div class = "ok"><input id = "ok" type = "button" value = "Ok"></div>
                   <div class = "cancel"><input id = "cancel" button = "text" value = "Cancel"></div>
               </div>`;
}

I add this popup to the page same way:

document.getElementbyId("content").innerHTML = editUserTemplate(user);

and add am eventlistener

const inputs = document.querySelectorAll('.input');
inputs.foreach( input =>
{
  input.addEventListener('change', (ev) =>
  {
      const element = ev.target;
      user[element.id] = element.value;
  })
})

now we need a listener to the buttons (I will only add one for the ok here)

document.getElementBy('ok').addEventListener('click', () =>
{
    document.getElementbyId("content").innerHTML = userTemplate(user);
}

and here we have the problem. Now I can put everything in the input for example

<img src='x' onerror='alert(1)'>

which will execute the alert command.

So what will be the best solution to avoid this?

Removing all tags from the users input is no option. Lets say the user is a company named <Files&Sons> or something like that? so the tag symbols must be possible to use for the user.
Of course I could go away from the string template idea and use textContent() instead for each attribute to show. But at the end string templates are very nice and easy to use.

I think that the main problem comes from user input being output as if it were HTML code. As a result, all text needs to be sanitized before being output to the DOM.

A good page that takes you through the details is https://gomakethings.com/how-to-sanitize-html-strings-with-vanilla-js-to-reduce-your-risk-of-xss-attacks/

1 Like

Thanks, looks like there is nothing like a perfect solution. Its always something like “Hopefully the hackers will not find a new way” :slight_smile:

Also this will not solve the problem that <Files&Son> would not work

In Angular there is a service dedicated to this very purpose. You actually can’t even do what you are doing the template language prevents it unless the string is passed through the sanitizer to bypass.

Using innerHTML to inject dynamic content like that is a huge security vulnerability unless you can trust users not to be malicous. Another option might be validate that the text is safe at the server level when saving it to the persistent storage mechanism.

Validation of user input on backend side is of course implemented.
This is the first Time I come to the point to display data inserted from users without the backend in-between.
For me the easiest way is, to add a backend call, send data and received the secured data before displaying it.
But When I was developing this I was wondering what could be the alternatives, that’s why I asked here.

Can you share your back-end validation logic that would prevent users from entering xss attack strings.

I don’t know if this is the equivalent of sticking a band aid on, but one for your consideration

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.